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