diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 413dbfc5..5cc4dda9 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -208,6 +208,21 @@ update_filterable_attributes_1: |- ]) reset_filterable_attributes_1: |- client.index('movies').reset_filterable_attributes() +get_foreign_keys_setting_1: |- + client.index('books').get_foreign_keys() +update_foreign_keys_setting_1: |- + client.index('books').update_foreign_keys([ + { + 'foreignIndexUid': 'authors', + 'fieldName': 'author' + }, + { + 'foreignIndexUid': 'authors', + 'fieldName': 'related_authors' + } + ]) +reset_foreign_keys_setting_1: |- + client.index('books').reset_foreign_keys() get_searchable_attributes_1: |- client.index('movies').get_searchable_attributes() update_searchable_attributes_1: |- diff --git a/meilisearch/config.py b/meilisearch/config.py index 8cc59496..fd53a50e 100644 --- a/meilisearch/config.py +++ b/meilisearch/config.py @@ -28,6 +28,7 @@ class Paths: synonyms = "synonyms" accept_new_fields = "accept-new-fields" filterable_attributes = "filterable-attributes" + foreign_keys = "foreign-keys" sortable_attributes = "sortable-attributes" typo_tolerance = "typo-tolerance" dumps = "dumps" diff --git a/meilisearch/index.py b/meilisearch/index.py index db525a77..84cf5c85 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -1237,6 +1237,7 @@ def update_settings( - 'searchCutoffMs': Maximum search time in milliseconds - 'proximityPrecision': Precision for proximity ranking - 'localizedAttributes': Settings for localized attributes + - 'foreignKeys': List of foreign key relationships to other indexes More information: https://www.meilisearch.com/docs/reference/api/settings#update-settings @@ -1715,6 +1716,64 @@ def reset_filterable_attributes(self) -> TaskInfo: return TaskInfo(**task) + def get_foreign_keys(self) -> list[dict[str, str]]: + """Get foreign keys of the index. + + Returns + ------- + settings: + List containing the foreign keys of the index + + Raises + ------ + MeilisearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors + """ + return self.http.get(self.__settings_url_for(self.config.paths.foreign_keys)) + + def update_foreign_keys(self, body: list[dict[str, str]] | None) -> TaskInfo: + """Update foreign keys of the index. + + Parameters + ---------- + body: + List containing the foreign keys, each a dict with 'foreignIndexUid' and 'fieldName'. + + Returns + ------- + task_info: + TaskInfo instance containing information about a task to track the progress of an asynchronous process. + https://www.meilisearch.com/docs/reference/api/tasks#get-one-task + + Raises + ------ + MeilisearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors + """ + task = self.http.put(self.__settings_url_for(self.config.paths.foreign_keys), body) + + return TaskInfo(**task) + + def reset_foreign_keys(self) -> TaskInfo: + """Reset foreign keys of the index to default values. + + Returns + ------- + task_info: + TaskInfo instance containing information about a task to track the progress of an asynchronous process. + https://www.meilisearch.com/docs/reference/api/tasks#get-one-task + + Raises + ------ + MeilisearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors + """ + task = self.http.delete( + self.__settings_url_for(self.config.paths.foreign_keys), + ) + + return TaskInfo(**task) + # SORTABLE ATTRIBUTES SUB-ROUTES def get_sortable_attributes(self) -> list[str]: diff --git a/tests/conftest.py b/tests/conftest.py index 9d630286..8379c319 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -369,3 +369,20 @@ def enable_render_route(): json={"renderRoute": False}, timeout=10, ) + + +@fixture +def enable_foreign_keys(): + requests.patch( + f"{common.BASE_URL}/experimental-features", + headers={"Authorization": f"Bearer {common.MASTER_KEY}"}, + json={"foreignKeys": True}, + timeout=10, + ) + yield + requests.patch( + f"{common.BASE_URL}/experimental-features", + headers={"Authorization": f"Bearer {common.MASTER_KEY}"}, + json={"foreignKeys": False}, + timeout=10, + ) diff --git a/tests/settings/test_settings_foreign_keys_meilisearch.py b/tests/settings/test_settings_foreign_keys_meilisearch.py new file mode 100644 index 00000000..b893da09 --- /dev/null +++ b/tests/settings/test_settings_foreign_keys_meilisearch.py @@ -0,0 +1,59 @@ +FOREIGN_KEYS = [ + {"foreignIndexUid": "authors", "fieldName": "author"}, +] + + +def test_get_foreign_keys(empty_index, enable_foreign_keys): + """Tests getting the foreign keys.""" + response = empty_index().get_foreign_keys() + assert response == [] + + +def test_update_foreign_keys(empty_index, enable_foreign_keys): + """Tests updating the foreign keys.""" + index = empty_index() + response = index.update_foreign_keys(FOREIGN_KEYS) + update = index.wait_for_task(response.task_uid) + assert update.status == "succeeded" + + get_keys = index.get_foreign_keys() + assert len(get_keys) == len(FOREIGN_KEYS) + for key in FOREIGN_KEYS: + assert key in get_keys + + +def test_update_foreign_keys_to_none(empty_index, enable_foreign_keys): + """Tests updating the foreign keys at null.""" + index = empty_index() + # Update the settings first + response = index.update_foreign_keys(FOREIGN_KEYS) + update = index.wait_for_task(response.task_uid) + assert update.status == "succeeded" + # Check the settings have been correctly updated + get_keys = index.get_foreign_keys() + for key in FOREIGN_KEYS: + assert key in get_keys + # Launch test to update at null the setting + response = index.update_foreign_keys(None) + index.wait_for_task(response.task_uid) + response = index.get_foreign_keys() + assert response == [] + + +def test_reset_foreign_keys(empty_index, enable_foreign_keys): + """Tests resetting the foreign keys setting to its default value""" + index = empty_index() + # Update the settings first + response = index.update_foreign_keys(FOREIGN_KEYS) + update = index.wait_for_task(response.task_uid) + assert update.status == "succeeded" + # Check the settings have been correctly updated + get_keys = index.get_foreign_keys() + assert len(get_keys) == len(FOREIGN_KEYS) + for key in FOREIGN_KEYS: + assert key in get_keys + # Check the reset of the settings + response = index.reset_foreign_keys() + index.wait_for_task(response.task_uid) + response = index.get_foreign_keys() + assert response == []