Skip to content

Commit 1fa08e0

Browse files
authored
Feat: Custom Dictionaries (#445)
* feat(cust-dictionaries): start implementing methods and CTS * feat(custom-dictionaries): finish tests * feat(dictionaries): adds doc blocks and wrap up tests * fix PR
1 parent 0abb796 commit 1fa08e0

File tree

4 files changed

+301
-2
lines changed

4 files changed

+301
-2
lines changed

lib/algolia.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
require 'algolia/responses/add_api_key_response'
2121
require 'algolia/responses/update_api_key_response'
2222
require 'algolia/responses/delete_api_key_response'
23+
require 'algolia/responses/dictionary_response'
2324
require 'algolia/responses/restore_api_key_response'
2425
require 'algolia/responses/multiple_batch_indexing_response'
2526
require 'algolia/responses/multiple_response'
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/algolia/search_client.rb

Lines changed: 175 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def get_task_status(index_name, task_id, opts = {})
8080
end
8181

8282
# # # # # # # # # # # # # # # # # # # # #
83-
# MISC
83+
# INDEX METHODS
8484
# # # # # # # # # # # # # # # # # # # # #
8585

8686
# Initialize an index with a given name
@@ -594,12 +594,185 @@ def pending_mappings?(opts = {})
594594
@transporter.read(:GET, '/1/clusters/mapping/pending' + handle_params({ getClusters: retrieve_mappings }), {}, request_options)
595595
end
596596

597-
#
598597
# Aliases the pending_mappings? method
599598
#
600599
alias_method :has_pending_mappings, :pending_mappings?
601600

601+
# # # # # # # # # # # # # # # # # # # # #
602+
# CUSTOM DICTIONARIES METHODS
603+
# # # # # # # # # # # # # # # # # # # # #
604+
605+
# Save entries for a given dictionary
606+
#
607+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
608+
# @param dictionary_entries [Array<Hash>] array of dictionary entries
609+
# @param opts [Hash] contains extra parameters to send with your query
610+
#
611+
# @return DictionaryResponse
612+
#
613+
def save_dictionary_entries(dictionary, dictionary_entries, opts = {})
614+
response = @transporter.write(
615+
:POST,
616+
path_encode('/1/dictionaries/%s/batch', dictionary),
617+
{ clearExistingDictionaryEntries: false, requests: chunk('addEntry', dictionary_entries) },
618+
opts
619+
)
620+
621+
DictionaryResponse.new(self, response)
622+
end
623+
624+
# Save entries for a given dictionary and wait for the task to finish
625+
#
626+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
627+
# @param dictionary_entries [Array<Hash>] array of dictionary entries
628+
# @param opts [Hash] contains extra parameters to send with your query
629+
#
630+
def save_dictionary_entries!(dictionary, dictionary_entries, opts = {})
631+
response = save_dictionary_entries(dictionary, dictionary_entries, opts)
632+
633+
response.wait(opts)
634+
end
635+
636+
# Replace entries for a given dictionary
637+
#
638+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
639+
# @param dictionary_entries [Array<Hash>] array of dictionary entries
640+
# @param opts [Hash] contains extra parameters to send with your query
641+
#
642+
# @return DictionaryResponse
643+
#
644+
def replace_dictionary_entries(dictionary, dictionary_entries, opts = {})
645+
response = @transporter.write(
646+
:POST,
647+
path_encode('/1/dictionaries/%s/batch', dictionary),
648+
{ clearExistingDictionaryEntries: true, requests: chunk('addEntry', dictionary_entries) },
649+
opts
650+
)
651+
652+
DictionaryResponse.new(self, response)
653+
end
654+
655+
# Replace entries for a given dictionary and wait for the task to finish
656+
#
657+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
658+
# @param dictionary_entries [Array<Hash>] array of dictionary entries
659+
# @param opts [Hash] contains extra parameters to send with your query
660+
#
661+
def replace_dictionary_entries!(dictionary, dictionary_entries, opts = {})
662+
response = replace_dictionary_entries(dictionary, dictionary_entries, opts)
663+
664+
response.wait(opts)
665+
end
666+
667+
# Delete entries for a given dictionary
668+
#
669+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
670+
# @param object_ids [Array<Hash>] array of object ids
671+
# @param opts [Hash] contains extra parameters to send with your query
672+
#
673+
# @return DictionaryResponse
674+
#
675+
def delete_dictionary_entries(dictionary, object_ids, opts = {})
676+
request = object_ids.map do |object_id|
677+
{ objectID: object_id }
678+
end
679+
response = @transporter.write(
680+
:POST,
681+
path_encode('/1/dictionaries/%s/batch', dictionary),
682+
{ clearExistingDictionaryEntries: false, requests: chunk('deleteEntry', request) },
683+
opts
684+
)
685+
686+
DictionaryResponse.new(self, response)
687+
end
688+
689+
# Delete entries for a given dictionary and wait for the task to finish
690+
#
691+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
692+
# @param object_ids [Array<Hash>] array of object ids
693+
# @param opts [Hash] contains extra parameters to send with your query
602694
#
695+
def delete_dictionary_entries!(dictionary, object_ids, opts = {})
696+
response = delete_dictionary_entries(dictionary, object_ids, opts)
697+
698+
response.wait(opts)
699+
end
700+
701+
# Clear all entries for a given dictionary
702+
#
703+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
704+
# @param opts [Hash] contains extra parameters to send with your query
705+
#
706+
# @return DictionaryResponse
707+
#
708+
def clear_dictionary_entries(dictionary, opts = {})
709+
replace_dictionary_entries(dictionary, [], opts)
710+
end
711+
712+
# Clear all entries for a given dictionary and wait for the task to finish
713+
#
714+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
715+
# @param opts [Hash] contains extra parameters to send with your query
716+
#
717+
def clear_dictionary_entries!(dictionary, opts = {})
718+
response = replace_dictionary_entries(dictionary, [], opts)
719+
720+
response.wait(opts)
721+
end
722+
723+
# Search entries for a given dictionary
724+
#
725+
# @param dictionary [String] dictionary name. Can be either 'stopwords', 'plurals' or 'compounds'
726+
# @param query [String] query to send
727+
# @param opts [Hash] contains extra parameters to send with your query
728+
#
729+
def search_dictionary_entries(dictionary, query, opts = {})
730+
@transporter.read(
731+
:POST,
732+
path_encode('/1/dictionaries/%s/search', dictionary),
733+
{ query: query },
734+
opts
735+
)
736+
end
737+
738+
# Set settings for all the dictionaries
739+
#
740+
# @param dictionary_settings [Hash]
741+
# @param opts [Hash] contains extra parameters to send with your query
742+
#
743+
# @return DictionaryResponse
744+
#
745+
def set_dictionary_settings(dictionary_settings, opts = {})
746+
response = @transporter.write(:PUT, '/1/dictionaries/*/settings', dictionary_settings, opts)
747+
748+
DictionaryResponse.new(self, response)
749+
end
750+
751+
# Set settings for all the dictionaries and wait for the task to finish
752+
#
753+
# @param dictionary_settings [Hash]
754+
# @param opts [Hash] contains extra parameters to send with your query
755+
#
756+
# @return DictionaryResponse
757+
#
758+
def set_dictionary_settings!(dictionary_settings, opts = {})
759+
response = set_dictionary_settings(dictionary_settings, opts)
760+
761+
response.wait(opts)
762+
end
763+
764+
# Retrieve settings for all the dictionaries
765+
#
766+
# @param opts [Hash] contains extra parameters to send with your query
767+
#
768+
def get_dictionary_settings(opts = {})
769+
@transporter.read(:GET, '/1/dictionaries/*/settings', {}, opts)
770+
end
771+
772+
# # # # # # # # # # # # # # # # # # # # #
773+
# MISC METHODS
774+
# # # # # # # # # # # # # # # # # # # # #
775+
603776
# Method available to make custom requests to the API
604777
#
605778
def custom_request(data, uri, method, call_type, opts = {})

test/algolia/integration/search_client_test.rb

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require 'securerandom'
12
require_relative 'base_test'
23

34
class SearchClientTest < BaseTest
@@ -367,5 +368,96 @@ def test_expired_secured_api_keys
367368
assert_equal 'The SecuredAPIKey doesn\'t have a validUntil parameter.', exception.message
368369
end
369370
end
371+
372+
describe 'Custom Dictionaries' do
373+
def before_all
374+
@client = Algolia::Search::Client.create(APPLICATION_ID_2, ADMIN_KEY_2)
375+
end
376+
377+
def test_stopwords_dictionaries
378+
entry_id = SecureRandom.hex
379+
assert_equal 0, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
380+
381+
entry = {
382+
objectID: entry_id,
383+
language: 'en',
384+
word: 'down'
385+
}
386+
@client.save_dictionary_entries!('stopwords', [entry])
387+
388+
stopwords = @client.search_dictionary_entries('stopwords', entry_id)
389+
assert_equal 1, stopwords[:nbHits]
390+
assert_equal stopwords[:hits][0][:objectID], entry[:objectID]
391+
assert_equal stopwords[:hits][0][:word], entry[:word]
392+
393+
@client.delete_dictionary_entries!('stopwords', [entry_id])
394+
assert_equal 0, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
395+
396+
old_dictionary_state = @client.search_dictionary_entries('stopwords', '')
397+
old_dictionary_entries = old_dictionary_state[:hits].map do |hit|
398+
hit.reject { |key| key == :type }
399+
end
400+
401+
@client.save_dictionary_entries!('stopwords', [entry])
402+
assert_equal 1, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
403+
404+
@client.replace_dictionary_entries!('stopwords', old_dictionary_entries)
405+
assert_equal 0, @client.search_dictionary_entries('stopwords', entry_id)[:nbHits]
406+
407+
stopwords_settings = {
408+
disableStandardEntries: {
409+
stopwords: {
410+
en: true
411+
}
412+
}
413+
}
414+
415+
@client.set_dictionary_settings!(stopwords_settings)
416+
417+
assert_equal @client.get_dictionary_settings, stopwords_settings
418+
end
419+
420+
def test_plurals_dictionaries
421+
entry_id = SecureRandom.hex
422+
assert_equal 0, @client.search_dictionary_entries('plurals', entry_id)[:nbHits]
423+
424+
entry = {
425+
objectID: entry_id,
426+
language: 'fr',
427+
words: %w(cheval chevaux)
428+
}
429+
@client.save_dictionary_entries!('plurals', [entry])
430+
431+
plurals = @client.search_dictionary_entries('plurals', entry_id)
432+
assert_equal 1, plurals[:nbHits]
433+
assert_equal plurals[:hits][0][:objectID], entry[:objectID]
434+
assert_equal plurals[:hits][0][:words], entry[:words]
435+
436+
@client.delete_dictionary_entries!('plurals', [entry_id])
437+
assert_equal 0, @client.search_dictionary_entries('plurals', entry_id)[:nbHits]
438+
end
439+
440+
def test_compounds_dictionaries
441+
entry_id = SecureRandom.hex
442+
assert_equal 0, @client.search_dictionary_entries('compounds', entry_id)[:nbHits]
443+
444+
entry = {
445+
objectID: entry_id,
446+
language: 'de',
447+
word: 'kopfschmerztablette',
448+
decomposition: %w(kopf schmerz tablette)
449+
}
450+
@client.save_dictionary_entries!('compounds', [entry])
451+
452+
compounds = @client.search_dictionary_entries('compounds', entry_id)
453+
assert_equal 1, compounds[:nbHits]
454+
assert_equal compounds[:hits][0][:objectID], entry[:objectID]
455+
assert_equal compounds[:hits][0][:word], entry[:word]
456+
assert_equal compounds[:hits][0][:decomposition], entry[:decomposition]
457+
458+
@client.delete_dictionary_entries!('compounds', [entry_id])
459+
assert_equal 0, @client.search_dictionary_entries('compounds', entry_id)[:nbHits]
460+
end
461+
end
370462
end
371463
end

0 commit comments

Comments
 (0)