diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 226a7081..1f7ba884 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -109,7 +109,13 @@ update_settings_1: |- 'wolverine': ['xmen', 'logan'], 'logan': ['wolverine'] }, - 'acceptNewFields': False + 'typoTolerance': { + 'minWordSizeForTypos': { + 'oneTypo': 8, + 'twoTypos': 10 + }, + 'disableOnAttributes': ['title'] + } }) reset_settings_1: |- client.index('movies').reset_settings() @@ -180,6 +186,20 @@ update_displayed_attributes_1: |- ]) reset_displayed_attributes_1: |- client.index('movies').reset_displayed_attributes() +get_typo_tolerance_1: + client.index('books').get_typo_tolerance() +update_typo_tolerance_1: |- + client.index('books').update_typo_tolerance({ + 'minWordSizeForTypos': { + 'oneTypo': 4, + 'twoTypos': 10 + }, + 'disableOnAttributes': [ + 'title' + ] + }) +reset_typo_tolerance_1: |- + client.index('books').reset_typo_tolerance() get_sortable_attributes_1: |- client.index('books').get_sortable_attributes() update_sortable_attributes_1: |- @@ -243,12 +263,23 @@ search_parameter_guide_retrieve_1: |- search_parameter_guide_crop_1: |- client.index('movies').search('shifu', { 'attributesToCrop': ['overview'], - 'cropLength': 10 + 'cropLength': 5 + }) +search_parameter_guide_crop_marker_1: |- + client.index('movies').search('shifu', { + 'attributesToCrop': ['overview'], + 'cropMarker': '[…]' }) search_parameter_guide_highlight_1: |- client.index('movies').search('winter feast', { 'attributesToHighlight': ['overview'] }) +search_parameter_guide_highlight_tag_1: |- + client.index('movies').search('winter feast', { + 'attributesToHighlight': ['overview'], + 'highlightPreTag': '', + 'highlightPostTag': '' + }) search_parameter_guide_matches_1: |- client.index('movies').search('winter feast', { 'matches': 'true' @@ -309,6 +340,34 @@ settings_guide_sortable_1: |- 'author' ] }) +settings_guide_typo_tolerance_1: |- + client.index('movies').update_typo_tolerance({ + 'minWordSizeForTypos': { + 'twoTypos': 12 + }, + 'disableOnAttributes': [ + 'title' + ] + }) +typo_tolerance_guide_1: |- + client.index('movies').update_typo_tolerance({ + 'enabled': False + }) +typo_tolerance_guide_2: |- + client.index('movies').update_typo_tolerance({ + 'disableOnAttributes': ['title'] + }) +typo_tolerance_guide_3: |- + client.index('movies').update_typo_tolerance({ + 'disableOnWords': ['shrek'] + }) +typo_tolerance_guide_4: |- + client.index('movies').update_typo_tolerance({ + 'minWordSizeForTypos': { + 'oneTypo': 4, + 'twoTypos': 10 + } + }) add_movies_json_1: |- import json diff --git a/.github/workflows/pre-release-tests.yml b/.github/workflows/pre-release-tests.yml index fc4c8900..7379ba75 100644 --- a/.github/workflows/pre-release-tests.yml +++ b/.github/workflows/pre-release-tests.yml @@ -29,6 +29,6 @@ jobs: - name: Get the latest Meilisearch RC run: echo "MEILISEARCH_VERSION=$(curl https://raw.githubusercontent.com/meilisearch/integration-guides/main/scripts/get-latest-meilisearch-rc.sh | bash)" >> $GITHUB_ENV - name: Meilisearch (${{ env.MEILISEARCH_VERSION }}) setup with Docker - run: docker run -d -p 7700:7700 getmeili/meilisearch:${{ env.MEILISEARCH_VERSION }} ./meilisearch --master-key=masterKey --no-analytics + run: docker run -d -p 7700:7700 getmeili/meilisearch:${{ env.MEILISEARCH_VERSION }} meilisearch --master-key=masterKey --no-analytics - name: Test with pytest run: pipenv run pytest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4833c362..743da4df 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,7 +31,7 @@ jobs: - name: Install dependencies run: pipenv install --dev - name: Meilisearch (latest version) setup with Docker - run: docker run -d -p 7700:7700 getmeili/meilisearch:latest ./meilisearch --no-analytics --master-key=masterKey + run: docker run -d -p 7700:7700 getmeili/meilisearch:latest meilisearch --no-analytics --master-key=masterKey - name: Test with pytest run: pipenv run pytest diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e9c5899..5784f664 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ Optionally tox can be used to run test on all supported version of Python, mypy, ```bash docker pull getmeili/meilisearch:latest # Fetch the latest version of Meilisearch image from Docker Hub -docker run -p 7700:7700 getmeili/meilisearch:latest ./meilisearch --master-key=masterKey --no-analytics +docker run -p 7700:7700 getmeili/meilisearch:latest meilisearch --master-key=masterKey --no-analytics pipenv run tox ``` diff --git a/README.md b/README.md index 6f70b9f8..4089e4a9 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ index.search( ## 🤖 Compatibility with Meilisearch -This package only guarantees the compatibility with the [version v0.26.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.26.0). +This package only guarantees the compatibility with the [version v0.27.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.27.0). ## 💡 Learn More diff --git a/datasets/nested_movies.json b/datasets/nested_movies.json new file mode 100644 index 00000000..68a97335 --- /dev/null +++ b/datasets/nested_movies.json @@ -0,0 +1,54 @@ +[ + { + "id": 1, + "title": "Pride and Prejudice", + "info": { + "comment": "A great book", + "reviewNb": 50 + } + }, + { + "id": 2, + "title": "Le Petit Prince", + "info": { + "comment": "A french book", + "reviewNb": 600 + } + }, + { + "id": 3, + "title": "Le Rouge et le Noir", + "info": { + "comment": "Another french book", + "reviewNb": 700 + } + }, + { + "id": 4, + "title": "Alice In Wonderland", + "comment": "A weird book", + "info": { + "comment": "A weird book", + "reviewNb": 800 + } + }, + { + "id": 5, + "title": "The Hobbit", + "info": { + "comment": "An awesome book", + "reviewNb": 900 + } + }, + { + "id": 6, + "title": "Harry Potter and the Half-Blood Prince", + "info": { + "comment": "The best book", + "reviewNb": 1000 + } + }, + { "id": 7, + "title": "The Hitchhiker's Guide to the Galaxy" + } +] diff --git a/meilisearch/config.py b/meilisearch/config.py index 67a4e566..9b6442be 100644 --- a/meilisearch/config.py +++ b/meilisearch/config.py @@ -25,6 +25,7 @@ class Paths(): accept_new_fields = 'accept-new-fields' filterable_attributes = 'filterable-attributes' sortable_attributes = 'sortable-attributes' + typo_tolerance = 'typo-tolerance' dumps = 'dumps' def __init__( diff --git a/meilisearch/index.py b/meilisearch/index.py index b07dd44b..0240f925 100644 --- a/meilisearch/index.py +++ b/meilisearch/index.py @@ -289,7 +289,7 @@ def add_documents( self, documents: List[Dict[str, Any]], primary_key: Optional[str] = None, - ) -> Dict[str, int]: + ) -> Dict[str, Any]: """Add documents to the index. Parameters @@ -318,7 +318,7 @@ def add_documents_in_batches( documents: List[Dict[str, Any]], batch_size: int = 1000, primary_key: Optional[str] = None, - ) -> List[Dict[str, int]]: + ) -> List[Dict[str, Any]]: """Add documents to the index in batches. Parameters @@ -355,7 +355,7 @@ def add_documents_json( self, str_documents: str, primary_key: Optional[str] = None, - ) -> Dict[str, int]: + ) -> Dict[str, Any]: """Add string documents from JSON file to the index. Parameters @@ -382,7 +382,7 @@ def add_documents_csv( self, str_documents: str, primary_key: Optional[str] = None, - ) -> Dict[str, int]: + ) -> Dict[str, Any]: """Add string documents from a CSV file to the index. Parameters @@ -409,7 +409,7 @@ def add_documents_ndjson( self, str_documents: str, primary_key: Optional[str] = None, - ) -> Dict[str, int]: + ) -> Dict[str, Any]: """Add string documents from a NDJSON file to the index. Parameters @@ -437,7 +437,7 @@ def add_documents_raw( str_documents: str, primary_key: Optional[str] = None, content_type: Optional[str] = None, - ) -> Dict[str, int]: + ) -> Dict[str, Any]: """Add string documents to the index. Parameters @@ -467,7 +467,7 @@ def update_documents( self, documents: List[Dict[str, Any]], primary_key: Optional[str] = None - ) -> Dict[str, int]: + ) -> Dict[str, Any]: """Update documents in the index. Parameters @@ -615,7 +615,7 @@ def get_settings(self) -> Dict[str, Any]: f'{self.config.paths.index}/{self.uid}/{self.config.paths.setting}' ) - def update_settings(self, body: Dict[str, Any]) -> Dict[str, int]: + def update_settings(self, body: Dict[str, Any]) -> Dict[str, Any]: """Update settings of the index. https://docs.meilisearch.com/reference/api/settings.html#update-settings @@ -643,7 +643,7 @@ def update_settings(self, body: Dict[str, Any]) -> Dict[str, int]: body ) - def reset_settings(self) -> Dict[str, int]: + def reset_settings(self) -> Dict[str, Any]: """Reset settings of the index to default values. https://docs.meilisearch.com/reference/api/settings.html#reset-settings @@ -666,8 +666,7 @@ def reset_settings(self) -> Dict[str, int]: # RANKING RULES SUB-ROUTES def get_ranking_rules(self) -> List[str]: - """ - Get ranking rules of the index. + """Get ranking rules of the index. Returns ------- @@ -683,9 +682,8 @@ def get_ranking_rules(self) -> List[str]: self.__settings_url_for(self.config.paths.ranking_rules) ) - def update_ranking_rules(self, body: List[str]) -> Dict[str, int]: - """ - Update ranking rules of the index. + def update_ranking_rules(self, body: List[str]) -> Dict[str, Any]: + """Update ranking rules of the index. Parameters ---------- @@ -708,7 +706,7 @@ def update_ranking_rules(self, body: List[str]) -> Dict[str, int]: body ) - def reset_ranking_rules(self) -> Dict[str, int]: + def reset_ranking_rules(self) -> Dict[str, Any]: """Reset ranking rules of the index to default values. Returns @@ -729,8 +727,7 @@ def reset_ranking_rules(self) -> Dict[str, int]: # DISTINCT ATTRIBUTE SUB-ROUTES def get_distinct_attribute(self) -> Optional[str]: - """ - Get distinct attribute of the index. + """Get distinct attribute of the index. Returns ------- @@ -746,9 +743,8 @@ def get_distinct_attribute(self) -> Optional[str]: self.__settings_url_for(self.config.paths.distinct_attribute) ) - def update_distinct_attribute(self, body: Dict[str, Any]) -> Dict[str, int]: - """ - Update distinct attribute of the index. + def update_distinct_attribute(self, body: Dict[str, Any]) -> Dict[str, Any]: + """Update distinct attribute of the index. Parameters ---------- @@ -771,7 +767,7 @@ def update_distinct_attribute(self, body: Dict[str, Any]) -> Dict[str, int]: body ) - def reset_distinct_attribute(self) -> Dict[str, int]: + def reset_distinct_attribute(self) -> Dict[str, Any]: """Reset distinct attribute of the index to default values. Returns @@ -792,8 +788,7 @@ def reset_distinct_attribute(self) -> Dict[str, int]: # SEARCHABLE ATTRIBUTES SUB-ROUTES def get_searchable_attributes(self) -> List[str]: - """ - Get searchable attributes of the index. + """Get searchable attributes of the index. Returns ------- @@ -809,9 +804,8 @@ def get_searchable_attributes(self) -> List[str]: self.__settings_url_for(self.config.paths.searchable_attributes) ) - def update_searchable_attributes(self, body: List[str]) -> Dict[str, int]: - """ - Update searchable attributes of the index. + def update_searchable_attributes(self, body: List[str]) -> Dict[str, Any]: + """Update searchable attributes of the index. Parameters ---------- @@ -834,7 +828,7 @@ def update_searchable_attributes(self, body: List[str]) -> Dict[str, int]: body ) - def reset_searchable_attributes(self) -> Dict[str, int]: + def reset_searchable_attributes(self) -> Dict[str, Any]: """Reset searchable attributes of the index to default values. Returns @@ -855,8 +849,7 @@ def reset_searchable_attributes(self) -> Dict[str, int]: # DISPLAYED ATTRIBUTES SUB-ROUTES def get_displayed_attributes(self) -> List[str]: - """ - Get displayed attributes of the index. + """Get displayed attributes of the index. Returns ------- @@ -872,9 +865,8 @@ def get_displayed_attributes(self) -> List[str]: self.__settings_url_for(self.config.paths.displayed_attributes) ) - def update_displayed_attributes(self, body: List[str]) -> Dict[str, int]: - """ - Update displayed attributes of the index. + def update_displayed_attributes(self, body: List[str]) -> Dict[str, Any]: + """Update displayed attributes of the index. Parameters ---------- @@ -897,7 +889,7 @@ def update_displayed_attributes(self, body: List[str]) -> Dict[str, int]: body ) - def reset_displayed_attributes(self) -> Dict[str, int]: + def reset_displayed_attributes(self) -> Dict[str, Any]: """Reset displayed attributes of the index to default values. Returns @@ -918,8 +910,7 @@ def reset_displayed_attributes(self) -> Dict[str, int]: # STOP WORDS SUB-ROUTES def get_stop_words(self) -> List[str]: - """ - Get stop words of the index. + """Get stop words of the index. Returns ------- @@ -935,9 +926,8 @@ def get_stop_words(self) -> List[str]: self.__settings_url_for(self.config.paths.stop_words) ) - def update_stop_words(self, body: List[str]) -> Dict[str, int]: - """ - Update stop words of the index. + def update_stop_words(self, body: List[str]) -> Dict[str, Any]: + """Update stop words of the index. Parameters ---------- @@ -960,7 +950,7 @@ def update_stop_words(self, body: List[str]) -> Dict[str, int]: body ) - def reset_stop_words(self) -> Dict[str, int]: + def reset_stop_words(self) -> Dict[str, Any]: """Reset stop words of the index to default values. Returns @@ -981,8 +971,7 @@ def reset_stop_words(self) -> Dict[str, int]: # SYNONYMS SUB-ROUTES def get_synonyms(self) -> Dict[str, List[str]]: - """ - Get synonyms of the index. + """Get synonyms of the index. Returns ------- @@ -998,9 +987,8 @@ def get_synonyms(self) -> Dict[str, List[str]]: self.__settings_url_for(self.config.paths.synonyms) ) - def update_synonyms(self, body: Dict[str, List[str]]) -> Dict[str, int]: - """ - Update synonyms of the index. + def update_synonyms(self, body: Dict[str, List[str]]) -> Dict[str, Any]: + """Update synonyms of the index. Parameters ---------- @@ -1023,7 +1011,7 @@ def update_synonyms(self, body: Dict[str, List[str]]) -> Dict[str, int]: body ) - def reset_synonyms(self) -> Dict[str, int]: + def reset_synonyms(self) -> Dict[str, Any]: """Reset synonyms of the index to default values. Returns @@ -1044,8 +1032,7 @@ def reset_synonyms(self) -> Dict[str, int]: # FILTERABLE ATTRIBUTES SUB-ROUTES def get_filterable_attributes(self) -> List[str]: - """ - Get filterable attributes of the index. + """Get filterable attributes of the index. Returns ------- @@ -1061,9 +1048,8 @@ def get_filterable_attributes(self) -> List[str]: self.__settings_url_for(self.config.paths.filterable_attributes) ) - def update_filterable_attributes(self, body: List[str]) -> Dict[str, int]: - """ - Update filterable attributes of the index. + def update_filterable_attributes(self, body: List[str]) -> Dict[str, Any]: + """Update filterable attributes of the index. Parameters ---------- @@ -1086,7 +1072,7 @@ def update_filterable_attributes(self, body: List[str]) -> Dict[str, int]: body ) - def reset_filterable_attributes(self) -> Dict[str, int]: + def reset_filterable_attributes(self) -> Dict[str, Any]: """Reset filterable attributes of the index to default values. Returns @@ -1108,8 +1094,7 @@ def reset_filterable_attributes(self) -> Dict[str, int]: # SORTABLE ATTRIBUTES SUB-ROUTES def get_sortable_attributes(self) -> List[str]: - """ - Get sortable attributes of the index. + """Get sortable attributes of the index. Returns ------- @@ -1125,9 +1110,8 @@ def get_sortable_attributes(self) -> List[str]: self.__settings_url_for(self.config.paths.sortable_attributes) ) - def update_sortable_attributes(self, body: List[str]) -> Dict[str, int]: - """ - Update sortable attributes of the index. + def update_sortable_attributes(self, body: List[str]) -> Dict[str, Any]: + """Update sortable attributes of the index. Parameters ---------- @@ -1150,7 +1134,7 @@ def update_sortable_attributes(self, body: List[str]) -> Dict[str, int]: body ) - def reset_sortable_attributes(self) -> Dict[str, int]: + def reset_sortable_attributes(self) -> Dict[str, Any]: """Reset sortable attributes of the index to default values. Returns @@ -1168,6 +1152,67 @@ def reset_sortable_attributes(self) -> Dict[str, int]: self.__settings_url_for(self.config.paths.sortable_attributes), ) + # TYPO TOLERANCE SUB-ROUTES + + def get_typo_tolerance(self) -> Dict[str, Any]: + """Get typo tolerance of the index. + + Returns + ------- + settings: dict + Dictionary containing the typo tolerance of the index. + + Raises + ------ + MeiliSearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors + """ + return self.http.get( + self.__settings_url_for(self.config.paths.typo_tolerance) + ) + + def update_typo_tolerance(self, body: Dict[str, Any]) -> Dict[str, Any]: + """Update typo tolerance of the index. + + Parameters + ---------- + body: dict + Dictionary containing the typo tolerance. + + Returns + ------- + task: + Dictionary containing a task to track the informations about the progress of an asynchronous process. + https://docs.meilisearch.com/reference/api/tasks.html#get-one-task + + Raises + ------ + MeiliSearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors + """ + return self.http.post( + self.__settings_url_for(self.config.paths.typo_tolerance), + body + ) + + def reset_typo_tolerance(self) -> Dict[str, Any]: + """Reset typo tolerance of the index to default values. + + Returns + ------- + task: + Dictionary containing a task to track the informations about the progress of an asynchronous process. + https://docs.meilisearch.com/reference/api/tasks.html#get-one-task + + Raises + ------ + MeiliSearchApiError + An error containing details about why Meilisearch can't process your request. Meilisearch error codes are described here: https://docs.meilisearch.com/errors/#meilisearch-errors + """ + return self.http.delete( + self.__settings_url_for(self.config.paths.typo_tolerance), + ) + @staticmethod def _batch( documents: List[Dict[str, Any]], batch_size: int diff --git a/tests/conftest.py b/tests/conftest.py index 43124544..37f9dbb8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -67,6 +67,14 @@ def songs_ndjson(): with open('./datasets/songs.ndjson', 'r', encoding='utf-8') as song_ndjson_file: return song_ndjson_file.read().encode('utf-8') +@fixture(scope='session') +def nested_movies(): + """ + Runs once per session. Provides the content of nested_movies.json. + """ + with open('./datasets/nested_movies.json', 'r', encoding='utf-8') as nested_movie_file: + yield json.loads(nested_movie_file.read()) + @fixture(scope='function') def empty_index(client, index_uid: Optional[str] = None): index_uid = index_uid if index_uid else common.INDEX_UID diff --git a/tests/index/test_index_search_meilisearch.py b/tests/index/test_index_search_meilisearch.py index 9886c908..c980ff9c 100644 --- a/tests/index/test_index_search_meilisearch.py +++ b/tests/index/test_index_search_meilisearch.py @@ -113,6 +113,72 @@ def test_custom_search_params_with_string_list(index_with_documents): assert 'title' in response['hits'][0]['_formatted'] assert 'overview' in response['hits'][0]['_formatted'] +def test_custom_search_params_with_crop_marker(index_with_documents): + """Tests search with a list of one string in query params.""" + response = index_with_documents().search( + 'dragon', + { + 'limit': 1, + 'attributesToCrop': ['overview'], + 'cropLength': 10, + } + ) + assert isinstance(response, dict) + assert len(response['hits']) == 1 + assert '_formatted' in response['hits'][0] + assert 'overview' in response['hits'][0]['_formatted'] + assert response['hits'][0]['_formatted']['overview'].count(' ') < 10 + assert response['hits'][0]['_formatted']['overview'].count('…') == 2 + +def test_custom_search_params_with_customized_crop_marker(index_with_documents): + """Tests search with a list of one string in query params.""" + response = index_with_documents().search( + 'dragon', + { + 'limit': 1, + 'attributesToCrop': ['overview'], + 'cropLength': 10, + 'cropMarker': '(ꈍᴗꈍ)', + } + ) + assert isinstance(response, dict) + assert len(response['hits']) == 1 + assert '_formatted' in response['hits'][0] + assert 'overview' in response['hits'][0]['_formatted'] + assert response['hits'][0]['_formatted']['overview'].count('(ꈍᴗꈍ)') == 2 + +def test_custom_search_params_with_highlight_tag(index_with_documents): + """Tests search with a list of one string in query params.""" + response = index_with_documents().search( + 'dragon', + { + 'limit': 1, + 'attributesToHighlight': ['*'], + } + ) + assert isinstance(response, dict) + assert len(response['hits']) == 1 + assert '_formatted' in response['hits'][0] + assert 'title' in response['hits'][0]['_formatted'] + assert response['hits'][0]['_formatted']['title'] == 'How to Train Your Dragon: The Hidden World' + +def test_custom_search_params_with_customized_highlight_tag(index_with_documents): + """Tests search with a list of one string in query params.""" + response = index_with_documents().search( + 'dragon', + { + 'limit': 1, + 'attributesToHighlight': ['*'], + 'highlightPreTag': '(⊃。•́‿•̀。)⊃ ', + 'highlightPostTag': ' ⊂(´• ω •`⊂)', + } + ) + assert isinstance(response, dict) + assert len(response['hits']) == 1 + assert '_formatted' in response['hits'][0] + assert 'title' in response['hits'][0]['_formatted'] + assert response['hits'][0]['_formatted']['title'] == 'How to Train Your (⊃。•́‿•̀。)⊃ Dragon ⊂(´• ω •`⊂): The Hidden World' + def test_custom_search_params_with_facets_distribution(index_with_documents): index = index_with_documents() update = index.update_filterable_attributes(['genre']) @@ -350,3 +416,37 @@ def test_phrase_search(index_with_documents): assert 'release_date' in response['hits'][0] assert response['hits'][0]['title'] == 'Dumbo' assert '_formatted' not in response['hits'][0] + +def test_basic_search_on_nested_documents(index_with_documents, nested_movies): + """Tests search with an simple query on nested fields.""" + response = index_with_documents('nested_fields_index', nested_movies).search('An awesome') + assert isinstance(response, dict) + assert response['hits'][0]['id'] == 5 + assert len(response['hits']) == 1 + +def test_search_on_nested_documents_with_searchable_attributes(index_with_documents, nested_movies): + """Tests search on nested fields with searchable attribute.""" + index = index_with_documents('nested_fields_index', nested_movies) + response_searchable_attributes = index.update_searchable_attributes(['title', 'info.comment']) + index.wait_for_task(response_searchable_attributes['uid']) + response = index.search('An awesome') + assert isinstance(response, dict) + assert response['hits'][0]['id'] == 5 + assert len(response['hits']) == 1 + +def test_search_on_nested_documents_with_sortable_attributes(index_with_documents, nested_movies): + """Tests search on nested fields with searchable attribute and sortable attributes.""" + index = index_with_documents('nested_fields_index', nested_movies) + response_settings = index.update_settings({ + 'searchableAttributes': ['title', 'info.comment'], + 'sortableAttributes': ['info.reviewNb'], + }) + index.wait_for_task(response_settings['uid']) + response = index.search( + '', + { + 'sort': ['info.reviewNb:desc'] + } + ) + assert isinstance(response, dict) + assert response['hits'][0]['id'] == 6 diff --git a/tests/settings/test_settings.py b/tests/settings/test_settings.py index 6a0021ec..ada47012 100644 --- a/tests/settings/test_settings.py +++ b/tests/settings/test_settings.py @@ -13,12 +13,25 @@ 'exactness' ] +DEFAULT_TYPO_TOLERANCE = { + 'enabled': True, + 'minWordSizeForTypos': { + 'oneTypo': 5, + 'twoTypos': 9, + }, + 'disableOnWords': [], + 'disableOnAttributes': [], +} + def test_get_settings_default(empty_index): """Tests getting all settings by default.""" response = empty_index().get_settings() assert isinstance(response, dict) for rule in DEFAULT_RANKING_RULES: assert rule in response['rankingRules'] + for typo in DEFAULT_TYPO_TOLERANCE: + assert typo in response['typoTolerance'] + assert DEFAULT_TYPO_TOLERANCE[typo] == response['typoTolerance'][typo] assert response['distinctAttribute'] is None assert response['searchableAttributes'] == ['*'] assert response['displayedAttributes'] == ['*'] @@ -69,6 +82,9 @@ def test_reset_settings(empty_index): response = index.get_settings() for rule in DEFAULT_RANKING_RULES: assert rule in response['rankingRules'] + for typo in DEFAULT_TYPO_TOLERANCE: + assert typo in response['typoTolerance'] + assert DEFAULT_TYPO_TOLERANCE[typo] == response['typoTolerance'][typo] assert response['distinctAttribute'] is None assert response['displayedAttributes'] == ['*'] assert response['searchableAttributes'] == ['*'] diff --git a/tests/settings/test_settings_typo_tolerance_meilisearch.py b/tests/settings/test_settings_typo_tolerance_meilisearch.py new file mode 100644 index 00000000..612645fb --- /dev/null +++ b/tests/settings/test_settings_typo_tolerance_meilisearch.py @@ -0,0 +1,66 @@ +DEFAULT_TYPO_TOLERANCE = { + 'enabled': True, + 'minWordSizeForTypos': { + 'oneTypo': 5, + 'twoTypos': 9, + }, + 'disableOnWords': [], + 'disableOnAttributes': [], +} + +NEW_TYPO_TOLERANCE = { + 'enabled': True, + 'minWordSizeForTypos': { + 'oneTypo': 6, + 'twoTypos': 10, + }, + 'disableOnWords': [], + 'disableOnAttributes': ['title'], +} + +def test_get_typo_tolerance_default(empty_index): + """Tests getting default typo_tolerance.""" + response = empty_index().get_typo_tolerance() + + assert isinstance(response, dict) + assert response == DEFAULT_TYPO_TOLERANCE + +def test_update_typo_tolerance(empty_index): + """Tests updating typo_tolerance.""" + index = empty_index() + response_update = index.update_typo_tolerance(NEW_TYPO_TOLERANCE) + update = index.wait_for_task(response_update['uid']) + response_get = index.get_typo_tolerance() + + assert isinstance(response_update, dict) + assert 'uid' in response_update + assert update['status'] == 'succeeded' + assert isinstance(response_get, dict) + for typo_tolerance in NEW_TYPO_TOLERANCE: + assert typo_tolerance in response_get + assert NEW_TYPO_TOLERANCE[typo_tolerance] == response_get[typo_tolerance] + +def test_reset_typo_tolerance(empty_index): + """Tests resetting the typo_tolerance setting to its default value.""" + index = empty_index() + + # Update the settings + response_update = index.update_typo_tolerance(NEW_TYPO_TOLERANCE) + update1 = index.wait_for_task(response_update['uid']) + # Get the setting after update + response_get = index.get_typo_tolerance() + # Reset the setting + response_reset = index.reset_typo_tolerance() + update2 = index.wait_for_task(response_reset['uid']) + # Get the setting after reset + response_last = index.get_typo_tolerance() + + assert update1['status'] == 'succeeded' + assert isinstance(response_get, dict) + for typo_tolerance in NEW_TYPO_TOLERANCE: + assert typo_tolerance in response_get + assert NEW_TYPO_TOLERANCE[typo_tolerance] == response_get[typo_tolerance] + assert isinstance(response_reset, dict) + assert 'uid' in response_reset + assert update2['status'] == 'succeeded' + assert response_last == DEFAULT_TYPO_TOLERANCE