algolia 2.0.2 → 2.2.0

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