algolia 2.0.2 → 2.2.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.
@@ -0,0 +1,33 @@
1
+ module Algolia
2
+ class DictionaryResponse < BaseResponse
3
+ include CallType
4
+
5
+ attr_reader :raw_response
6
+
7
+ # @param client [Search::Client] Algolia Search Client used for verification
8
+ # @param response [Hash] Raw response from the client
9
+ #
10
+ def initialize(client, response)
11
+ @client = client
12
+ @raw_response = response
13
+ @done = false
14
+ end
15
+
16
+ # Wait for the task to complete
17
+ #
18
+ # @param opts [Hash] contains extra parameters to send with your query
19
+ #
20
+ def wait(_opts = {})
21
+ until @done
22
+ res = @client.custom_request({}, path_encode('/1/task/%s', @raw_response[:taskID]), :GET, READ)
23
+ status = get_option(res, 'status')
24
+ if status == 'published'
25
+ @done = true
26
+ end
27
+ sleep(Defaults::WAIT_TASK_DEFAULT_TIME_BEFORE_RETRY / 1000)
28
+ end
29
+
30
+ self
31
+ end
32
+ end
33
+ end
@@ -80,7 +80,7 @@ module Algolia
80
80
  end
81
81
 
82
82
  # # # # # # # # # # # # # # # # # # # # #
83
- # MISC
83
+ # INDEX METHODS
84
84
  # # # # # # # # # # # # # # # # # # # # #
85
85
 
86
86
  # Initialize an index with a given name
@@ -300,7 +300,7 @@ module Algolia
300
300
  # @return [AddApiKeyResponse]
301
301
  #
302
302
  def add_api_key!(acl, opts = {})
303
- response = add_api_key(acl)
303
+ response = add_api_key(acl, opts)
304
304
 
305
305
  response.wait(opts)
306
306
  end
@@ -480,6 +480,7 @@ module Algolia
480
480
  def multiple_queries(queries, opts = {})
481
481
  @transporter.read(:POST, '/1/indexes/*/queries', { requests: queries }, opts)
482
482
  end
483
+ alias_method :search, :multiple_queries
483
484
 
484
485
  # # # # # # # # # # # # # # # # # # # # #
485
486
  # MCM METHODS
@@ -594,12 +595,185 @@ module Algolia
594
595
  @transporter.read(:GET, '/1/clusters/mapping/pending' + handle_params({ getClusters: retrieve_mappings }), {}, request_options)
595
596
  end
596
597
 
597
- #
598
598
  # Aliases the pending_mappings? method
599
599
  #
600
600
  alias_method :has_pending_mappings, :pending_mappings?
601
601
 
602
+ # # # # # # # # # # # # # # # # # # # # #
603
+ # CUSTOM DICTIONARIES METHODS
604
+ # # # # # # # # # # # # # # # # # # # # #
605
+
606
+ # Save entries for a given dictionary
607
+ #
608
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
609
+ # @param dictionary_entries [Array<Hash>] array of dictionary entries
610
+ # @param opts [Hash] contains extra parameters to send with your query
611
+ #
612
+ # @return DictionaryResponse
613
+ #
614
+ def save_dictionary_entries(dictionary, dictionary_entries, opts = {})
615
+ response = @transporter.write(
616
+ :POST,
617
+ path_encode('/1/dictionaries/%s/batch', dictionary),
618
+ { clearExistingDictionaryEntries: false, requests: chunk('addEntry', dictionary_entries) },
619
+ opts
620
+ )
621
+
622
+ DictionaryResponse.new(self, response)
623
+ end
624
+
625
+ # Save entries for a given dictionary and wait for the task to finish
626
+ #
627
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
628
+ # @param dictionary_entries [Array<Hash>] array of dictionary entries
629
+ # @param opts [Hash] contains extra parameters to send with your query
630
+ #
631
+ def save_dictionary_entries!(dictionary, dictionary_entries, opts = {})
632
+ response = save_dictionary_entries(dictionary, dictionary_entries, opts)
633
+
634
+ response.wait(opts)
635
+ end
636
+
637
+ # Replace entries for a given dictionary
638
+ #
639
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
640
+ # @param dictionary_entries [Array<Hash>] array of dictionary entries
641
+ # @param opts [Hash] contains extra parameters to send with your query
642
+ #
643
+ # @return DictionaryResponse
644
+ #
645
+ def replace_dictionary_entries(dictionary, dictionary_entries, opts = {})
646
+ response = @transporter.write(
647
+ :POST,
648
+ path_encode('/1/dictionaries/%s/batch', dictionary),
649
+ { clearExistingDictionaryEntries: true, requests: chunk('addEntry', dictionary_entries) },
650
+ opts
651
+ )
652
+
653
+ DictionaryResponse.new(self, response)
654
+ end
655
+
656
+ # Replace entries for a given dictionary and wait for the task to finish
657
+ #
658
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
659
+ # @param dictionary_entries [Array<Hash>] array of dictionary entries
660
+ # @param opts [Hash] contains extra parameters to send with your query
661
+ #
662
+ def replace_dictionary_entries!(dictionary, dictionary_entries, opts = {})
663
+ response = replace_dictionary_entries(dictionary, dictionary_entries, opts)
664
+
665
+ response.wait(opts)
666
+ end
667
+
668
+ # Delete entries for a given dictionary
669
+ #
670
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
671
+ # @param object_ids [Array<Hash>] array of object ids
672
+ # @param opts [Hash] contains extra parameters to send with your query
673
+ #
674
+ # @return DictionaryResponse
675
+ #
676
+ def delete_dictionary_entries(dictionary, object_ids, opts = {})
677
+ request = object_ids.map do |object_id|
678
+ { objectID: object_id }
679
+ end
680
+ response = @transporter.write(
681
+ :POST,
682
+ path_encode('/1/dictionaries/%s/batch', dictionary),
683
+ { clearExistingDictionaryEntries: false, requests: chunk('deleteEntry', request) },
684
+ opts
685
+ )
686
+
687
+ DictionaryResponse.new(self, response)
688
+ end
689
+
690
+ # Delete entries for a given dictionary and wait for the task to finish
691
+ #
692
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
693
+ # @param object_ids [Array<Hash>] array of object ids
694
+ # @param opts [Hash] contains extra parameters to send with your query
602
695
  #
696
+ def delete_dictionary_entries!(dictionary, object_ids, opts = {})
697
+ response = delete_dictionary_entries(dictionary, object_ids, opts)
698
+
699
+ response.wait(opts)
700
+ end
701
+
702
+ # Clear all entries for a given dictionary
703
+ #
704
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
705
+ # @param opts [Hash] contains extra parameters to send with your query
706
+ #
707
+ # @return DictionaryResponse
708
+ #
709
+ def clear_dictionary_entries(dictionary, opts = {})
710
+ replace_dictionary_entries(dictionary, [], opts)
711
+ end
712
+
713
+ # Clear all entries for a given dictionary and wait for the task to finish
714
+ #
715
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
716
+ # @param opts [Hash] contains extra parameters to send with your query
717
+ #
718
+ def clear_dictionary_entries!(dictionary, opts = {})
719
+ response = replace_dictionary_entries(dictionary, [], opts)
720
+
721
+ response.wait(opts)
722
+ end
723
+
724
+ # Search entries for a given dictionary
725
+ #
726
+ # @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
727
+ # @param query [String] query to send
728
+ # @param opts [Hash] contains extra parameters to send with your query
729
+ #
730
+ def search_dictionary_entries(dictionary, query, opts = {})
731
+ @transporter.read(
732
+ :POST,
733
+ path_encode('/1/dictionaries/%s/search', dictionary),
734
+ { query: query },
735
+ opts
736
+ )
737
+ end
738
+
739
+ # Set settings for all the dictionaries
740
+ #
741
+ # @param dictionary_settings [Hash]
742
+ # @param opts [Hash] contains extra parameters to send with your query
743
+ #
744
+ # @return DictionaryResponse
745
+ #
746
+ def set_dictionary_settings(dictionary_settings, opts = {})
747
+ response = @transporter.write(:PUT, '/1/dictionaries/*/settings', dictionary_settings, opts)
748
+
749
+ DictionaryResponse.new(self, response)
750
+ end
751
+
752
+ # Set settings for all the dictionaries and wait for the task to finish
753
+ #
754
+ # @param dictionary_settings [Hash]
755
+ # @param opts [Hash] contains extra parameters to send with your query
756
+ #
757
+ # @return DictionaryResponse
758
+ #
759
+ def set_dictionary_settings!(dictionary_settings, opts = {})
760
+ response = set_dictionary_settings(dictionary_settings, opts)
761
+
762
+ response.wait(opts)
763
+ end
764
+
765
+ # Retrieve settings for all the dictionaries
766
+ #
767
+ # @param opts [Hash] contains extra parameters to send with your query
768
+ #
769
+ def get_dictionary_settings(opts = {})
770
+ @transporter.read(:GET, '/1/dictionaries/*/settings', {}, opts)
771
+ end
772
+
773
+ # # # # # # # # # # # # # # # # # # # # #
774
+ # MISC METHODS
775
+ # # # # # # # # # # # # # # # # # # # # #
776
+
603
777
  # Method available to make custom requests to the API
604
778
  #
605
779
  def custom_request(data, uri, method, call_type, opts = {})
@@ -987,7 +987,15 @@ module Algolia
987
987
  # @return [IndexingResponse]
988
988
  #
989
989
  def set_settings(settings, opts = {})
990
- response = @transporter.write(:PUT, path_encode('/1/indexes/%s/settings', @name), settings, opts)
990
+ request_options = symbolize_hash(opts)
991
+ forward_to_replicas = request_options.delete(:forwardToReplicas) || false
992
+
993
+ response = @transporter.write(
994
+ :PUT,
995
+ path_encode('/1/indexes/%s/settings', @name) + handle_params({ forwardToReplicas: forward_to_replicas }),
996
+ settings,
997
+ request_options
998
+ )
991
999
 
992
1000
  IndexingResponse.new(self, response)
993
1001
  end
@@ -1037,55 +1045,6 @@ module Algolia
1037
1045
 
1038
1046
  private
1039
1047
 
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
1048
  def raw_batch(requests, opts)
1090
1049
  @transporter.write(:POST, path_encode('/1/indexes/%s/batch', @name), { requests: requests }, opts)
1091
1050
  end
@@ -1,3 +1,3 @@
1
1
  module Algolia
2
- VERSION = '2.0.2'.freeze
2
+ VERSION = '2.2.0'.freeze
3
3
  end
data/lib/algolia.rb CHANGED
@@ -7,6 +7,7 @@ require 'algolia/config/base_config'
7
7
  require 'algolia/config/search_config'
8
8
  require 'algolia/config/analytics_config'
9
9
  require 'algolia/config/insights_config'
10
+ require 'algolia/config/recommend_config'
10
11
  require 'algolia/config/recommendation_config'
11
12
  require 'algolia/enums/call_type'
12
13
  require 'algolia/enums/retry_outcome_type'
@@ -20,6 +21,7 @@ require 'algolia/responses/indexing_response'
20
21
  require 'algolia/responses/add_api_key_response'
21
22
  require 'algolia/responses/update_api_key_response'
22
23
  require 'algolia/responses/delete_api_key_response'
24
+ require 'algolia/responses/dictionary_response'
23
25
  require 'algolia/responses/restore_api_key_response'
24
26
  require 'algolia/responses/multiple_batch_indexing_response'
25
27
  require 'algolia/responses/multiple_response'
@@ -32,6 +34,7 @@ require 'algolia/account_client'
32
34
  require 'algolia/search_client'
33
35
  require 'algolia/analytics_client'
34
36
  require 'algolia/insights_client'
37
+ require 'algolia/recommend_client'
35
38
  require 'algolia/recommendation_client'
36
39
  require 'algolia/error'
37
40
  require 'algolia/search_index'
@@ -23,7 +23,9 @@ class AnalyticsClientTest < BaseTest
23
23
  endAt: tomorrow.strftime('%Y-%m-%dT%H:%M:%SZ')
24
24
  }
25
25
 
26
- response = client.add_ab_test(ab_test)
26
+ response = retry_test do
27
+ client.add_ab_test(ab_test)
28
+ end
27
29
  ab_test_id = response[:abTestID]
28
30
 
29
31
  index1.wait_task(response[:taskID])
@@ -86,7 +88,9 @@ class AnalyticsClientTest < BaseTest
86
88
  endAt: tomorrow.strftime('%Y-%m-%dT%H:%M:%SZ')
87
89
  }
88
90
 
89
- response = client.add_ab_test(ab_test)
91
+ response = retry_test do
92
+ client.add_ab_test(ab_test)
93
+ end
90
94
  ab_test_id = response[:abTestID]
91
95
 
92
96
  index.wait_task(response[:taskID])
@@ -1,25 +1,27 @@
1
1
  class MockRequester
2
+ attr_accessor :requests
2
3
  def initialize
3
4
  @connection = nil
5
+ @requests = []
4
6
  end
5
7
 
6
- def send_request(host, method, path, _body, headers, _timeout, _connect_timeout)
7
- connection = get_connection(host)
8
- response = {
9
- connection: connection,
8
+ def send_request(host, method, path, body, headers, timeout, connect_timeout)
9
+ request = {
10
10
  host: host,
11
+ method: method,
11
12
  path: path,
13
+ body: body,
12
14
  headers: headers,
13
- method: method,
14
- status: 200,
15
- body: '{"hits":[],"nbHits":0,"page":0,"nbPages":1,"hitsPerPage":20,"exhaustiveNbHits":true,"query":"test","params":"query=test","processingTimeMS":1}',
16
- success: true
15
+ timeout: timeout,
16
+ connect_timeout: connect_timeout
17
17
  }
18
18
 
19
+ @requests.push(request)
20
+
19
21
  Algolia::Http::Response.new(
20
- status: response[:status],
21
- body: response[:body],
22
- headers: response[:headers]
22
+ status: 200,
23
+ body: '{"hits": []}',
24
+ headers: {}
23
25
  )
24
26
  end
25
27
 
@@ -0,0 +1,70 @@
1
+ require 'securerandom'
2
+ require_relative 'base_test'
3
+
4
+ class RecommendClientTest < BaseTest
5
+ describe 'Recommendations' do
6
+ def test_get_recommendations
7
+ requester = MockRequester.new
8
+ client = Algolia::Recommend::Client.new(@@search_config, http_requester: requester)
9
+
10
+ # It correctly formats queries using the 'bought-together' model
11
+ client.get_recommendations([{ indexName: 'products', objectID: 'B018APC4LE', model: Algolia::Recommend::Model::BOUGHT_TOGETHER }])
12
+
13
+ # It correctly formats queries using the 'related-products' model
14
+ client.get_recommendations([{ indexName: 'products', objectID: 'B018APC4LE', model: Algolia::Recommend::Model::RELATED_PRODUCTS }])
15
+
16
+ # It correctly formats multiple queries.
17
+ client.get_recommendations(
18
+ [
19
+ { indexName: 'products', objectID: 'B018APC4LE-1', model: Algolia::Recommend::Model::RELATED_PRODUCTS, threshold: 0 },
20
+ { indexName: 'products', objectID: 'B018APC4LE-2', model: Algolia::Recommend::Model::RELATED_PRODUCTS, threshold: 0 }
21
+ ]
22
+ )
23
+
24
+ # It resets the threshold to 0 if it's not numeric.
25
+ client.get_recommendations([{ indexName: 'products', objectID: 'B018APC4LE', model: Algolia::Recommend::Model::BOUGHT_TOGETHER, threshold: nil }])
26
+
27
+ # It passes the threshold correctly if it's numeric.
28
+ client.get_recommendations([{ indexName: 'products', objectID: 'B018APC4LE', model: Algolia::Recommend::Model::BOUGHT_TOGETHER, threshold: 42 }])
29
+
30
+ assert_requests(
31
+ requester,
32
+ [
33
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"bought-together","threshold":0}]}' },
34
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"related-products","threshold":0}]}' },
35
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE-1","model":"related-products","threshold":0},{"indexName":"products","objectID":"B018APC4LE-2","model":"related-products","threshold":0}]}' },
36
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"bought-together","threshold":0}]}' },
37
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"bought-together","threshold":42}]}' }
38
+ ]
39
+ )
40
+ end
41
+
42
+ def test_get_related_products
43
+ requester = MockRequester.new
44
+ client = Algolia::Recommend::Client.new(@@search_config, http_requester: requester)
45
+
46
+ client.get_related_products([{ indexName: 'products', objectID: 'B018APC4LE' }])
47
+
48
+ assert_requests(
49
+ requester,
50
+ [{ method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"related-products","threshold":0}]}' }]
51
+ )
52
+ end
53
+
54
+ def test_get_frequently_bought_together
55
+ requester = MockRequester.new
56
+ client = Algolia::Recommend::Client.new(@@search_config, http_requester: requester)
57
+
58
+ client.get_frequently_bought_together([{ indexName: 'products', objectID: 'B018APC4LE' }])
59
+ client.get_frequently_bought_together([{ indexName: 'products', objectID: 'B018APC4LE', fallbackParameters: {} }])
60
+
61
+ assert_requests(
62
+ requester,
63
+ [
64
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"bought-together","threshold":0}]}' },
65
+ { method: :post, path: '/1/indexes/*/recommendations', body: '{"requests":[{"indexName":"products","objectID":"B018APC4LE","model":"bought-together","threshold":0}]}' }
66
+ ]
67
+ )
68
+ end
69
+ end
70
+ end
@@ -1,3 +1,4 @@
1
+ require 'securerandom'
1
2
  require_relative 'base_test'
2
3
 
3
4
  class SearchClientTest < BaseTest
@@ -215,6 +216,7 @@ class SearchClientTest < BaseTest
215
216
 
216
217
  def test_api_keys
217
218
  assert_equal ['search'], @api_key[:acl]
219
+ assert_equal 'A description', @api_key[:description]
218
220
 
219
221
  api_keys = @@search_client.list_api_keys[:keys].map do |key|
220
222
  key[:value]
@@ -222,7 +224,9 @@ class SearchClientTest < BaseTest
222
224
  assert_includes api_keys, @api_key[:value]
223
225
 
224
226
  @@search_client.update_api_key!(@api_key[:value], { maxHitsPerQuery: 42 })
225
- updated_api_key = @@search_client.get_api_key(@api_key[:value])
227
+ updated_api_key = retry_test do
228
+ @@search_client.get_api_key(@api_key[:value], test: 'test')
229
+ end
226
230
  assert_equal 42, updated_api_key[:maxHitsPerQuery]
227
231
 
228
232
  @@search_client.delete_api_key!(@api_key[:value])
@@ -233,18 +237,13 @@ class SearchClientTest < BaseTest
233
237
 
234
238
  assert_equal 'Key does not exist', exception.message
235
239
 
236
- loop do
237
- begin
238
- @@search_client.restore_api_key!(@api_key[:value])
239
- break
240
- rescue Algolia::AlgoliaHttpError => e
241
- if e.code != 404
242
- raise StandardError
243
- end
244
- end
240
+ retry_test do
241
+ @@search_client.restore_api_key!(@api_key[:value])
245
242
  end
246
243
 
247
- restored_key = @@search_client.get_api_key(@api_key[:value])
244
+ restored_key = retry_test do
245
+ @@search_client.get_api_key(@api_key[:value])
246
+ end
248
247
 
249
248
  refute_nil restored_key
250
249
  end
@@ -333,7 +332,12 @@ class SearchClientTest < BaseTest
333
332
  secured_index1 = secured_client.init_index(@index1.name)
334
333
  secured_index2 = secured_client.init_index(@index2.name)
335
334
 
336
- secured_index1.search('')
335
+ res = retry_test do
336
+ secured_index1.search('')
337
+ end
338
+
339
+ assert_equal 1, res[:hits].length
340
+
337
341
  exception = assert_raises Algolia::AlgoliaHttpError do
338
342
  secured_index2.search('')
339
343
  end
@@ -366,5 +370,96 @@ class SearchClientTest < BaseTest
366
370
  assert_equal 'The SecuredAPIKey doesn\'t have a validUntil parameter.', exception.message
367
371
  end
368
372
  end
373
+
374
+ describe 'Custom Dictionaries' do
375
+ def before_all
376
+ @client = Algolia::Search::Client.create(APPLICATION_ID_2, ADMIN_KEY_2)
377
+ end
378
+
379
+ def test_stopwords_dictionaries
380
+ entry_id = SecureRandom.hex
381
+ assert_equal 0, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
382
+
383
+ entry = {
384
+ objectID: entry_id,
385
+ language: 'en',
386
+ word: 'down'
387
+ }
388
+ @client.save_dictionary_entries!('stopwords', [entry])
389
+
390
+ stopwords = @client.search_dictionary_entries('stopwords', entry_id)
391
+ assert_equal 1, stopwords[:nbHits]
392
+ assert_equal stopwords[:hits][0][:objectID], entry[:objectID]
393
+ assert_equal stopwords[:hits][0][:word], entry[:word]
394
+
395
+ @client.delete_dictionary_entries!('stopwords', [entry_id])
396
+ assert_equal 0, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
397
+
398
+ old_dictionary_state = @client.search_dictionary_entries('stopwords', '')
399
+ old_dictionary_entries = old_dictionary_state[:hits].map do |hit|
400
+ hit.reject { |key| key == :type }
401
+ end
402
+
403
+ @client.save_dictionary_entries!('stopwords', [entry])
404
+ assert_equal 1, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
405
+
406
+ @client.replace_dictionary_entries!('stopwords', old_dictionary_entries)
407
+ assert_equal 0, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
408
+
409
+ stopwords_settings = {
410
+ disableStandardEntries: {
411
+ stopwords: {
412
+ en: true
413
+ }
414
+ }
415
+ }
416
+
417
+ @client.set_dictionary_settings!(stopwords_settings)
418
+
419
+ assert_equal @client.get_dictionary_settings, stopwords_settings
420
+ end
421
+
422
+ def test_plurals_dictionaries
423
+ entry_id = SecureRandom.hex
424
+ assert_equal 0, @client.search_dictionary_entries('plurals', entry_id)[:nbHits]
425
+
426
+ entry = {
427
+ objectID: entry_id,
428
+ language: 'fr',
429
+ words: %w(cheval chevaux)
430
+ }
431
+ @client.save_dictionary_entries!('plurals', [entry])
432
+
433
+ plurals = @client.search_dictionary_entries('plurals', entry_id)
434
+ assert_equal 1, plurals[:nbHits]
435
+ assert_equal plurals[:hits][0][:objectID], entry[:objectID]
436
+ assert_equal plurals[:hits][0][:words], entry[:words]
437
+
438
+ @client.delete_dictionary_entries!('plurals', [entry_id])
439
+ assert_equal 0, @client.search_dictionary_entries('plurals', entry_id)[:nbHits]
440
+ end
441
+
442
+ def test_compounds_dictionaries
443
+ entry_id = SecureRandom.hex
444
+ assert_equal 0, @client.search_dictionary_entries('compounds', entry_id)[:nbHits]
445
+
446
+ entry = {
447
+ objectID: entry_id,
448
+ language: 'de',
449
+ word: 'kopfschmerztablette',
450
+ decomposition: %w(kopf schmerz tablette)
451
+ }
452
+ @client.save_dictionary_entries!('compounds', [entry])
453
+
454
+ compounds = @client.search_dictionary_entries('compounds', entry_id)
455
+ assert_equal 1, compounds[:nbHits]
456
+ assert_equal compounds[:hits][0][:objectID], entry[:objectID]
457
+ assert_equal compounds[:hits][0][:word], entry[:word]
458
+ assert_equal compounds[:hits][0][:decomposition], entry[:decomposition]
459
+
460
+ @client.delete_dictionary_entries!('compounds', [entry_id])
461
+ assert_equal 0, @client.search_dictionary_entries('compounds', entry_id)[:nbHits]
462
+ end
463
+ end
369
464
  end
370
465
  end
@@ -274,6 +274,9 @@ class SearchIndexTest < BaseTest
274
274
  @index.set_settings!(settings)
275
275
 
276
276
  assert_equal @index.get_settings, settings
277
+
278
+ # check that the forwardToReplicas parameter is passed correctly
279
+ assert @index.set_settings!(settings, { forwardToReplicas: true })
277
280
  end
278
281
  end
279
282