Skip to content

Commit

Permalink
Merge #327
Browse files Browse the repository at this point in the history
327: Changes related to the next MeiliSearch release (v0.23.0) r=alallema a=meili-bot

Related to this issue: meilisearch/integration-guides#142

This PR:
- gathers the changes related to the next MeiliSearch release (v0.23.0) so that this package is ready when the official release is out.
- should pass the tests against the [latest pre-release of MeiliSearch](https://github.com/meilisearch/MeiliSearch/releases).
- might eventually contain test failures until the MeiliSearch v0.23.0 is out.

⚠️ This PR should NOT be merged until the next release of MeiliSearch (v0.23.0) is out.

_This PR is auto-generated for the [pre-release week](https://github.com/meilisearch/integration-guides/blob/master/guides/pre-release-week.md) purpose._

Done:
- #329 
    - Add new methods:
        - `addDocumentsJson(string $documents, ?string $primaryKey = null)`
        - `addDocumentsNdJson(string $documents, ?string $primaryKey = null)`
        - `addDocumentsCsv(string $documents, ?string $primaryKey = null)`
    - Add tests for new methods
    - Remove json header `application/json` for every http methods
- #331 

Co-authored-by: meili-bot <74670311+meili-bot@users.noreply.github.com>
Co-authored-by: Clémentine Urquizar <clementine@meilisearch.com>
Co-authored-by: alallema <amelie@meilisearch.com>
Co-authored-by: Amélie <alallema@users.noreply.github.com>
  • Loading branch information
5 people authored Oct 12, 2021
2 parents 4b3af92 + 7ab5bf0 commit df74e09
Show file tree
Hide file tree
Showing 8 changed files with 954 additions and 24 deletions.
24 changes: 24 additions & 0 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,27 @@ search_parameter_guide_sort_1: |-
client.index('books').search('science fiction', {
'sort': ['price:asc']
})
geosearch_guide_filter_settings_1: |-
client.index('restaurants').update_filterable_attributes([
'_geo'
])
geosearch_guide_filter_usage_1: |-
client.index('restaurants').search('', {
'filter': '_geoRadius(45.4628328, 9.1076931, 2000)'
})
geosearch_guide_filter_usage_2: |-
client.index('restaurants').search('', {
'filter': '_geoRadius(45.4628328, 9.1076931, 2000) AND type = pizza'
})
geosearch_guide_sort_settings_1: |-
client.index('restaurants').update_sortable_attributes([
'_geo'
])
geosearch_guide_sort_usage_1: |-
client.index('restaurants').search('', {
'sort': ['_geoPoint(48.8583701,2.2922926):asc']
})
geosearch_guide_sort_usage_2: |-
client.index('restaurants').search('', {
'sort': ['_geoPoint(48.8583701,2.2922926):asc', 'rating:desc']
})
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ JSON output:

## 🤖 Compatibility with MeiliSearch

This package only guarantees the compatibility with the [version v0.22.0 of MeiliSearch](https://github.com/meilisearch/MeiliSearch/releases/tag/v0.22.0).
This package only guarantees the compatibility with the [version v0.23.0 of MeiliSearch](https://github.com/meilisearch/MeiliSearch/releases/tag/v0.23.0).

## 💡 Learn More

Expand Down
500 changes: 500 additions & 0 deletions datasets/songs.csv

Large diffs are not rendered by default.

225 changes: 225 additions & 0 deletions datasets/songs.ndjson

Large diffs are not rendered by default.

34 changes: 23 additions & 11 deletions meilisearch/_httprequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,33 @@ def __init__(self, config: Config) -> None:
self.config = config
self.headers = {
'X-Meili-Api-Key': self.config.api_key,
'Content-Type': 'application/json'
}

def send_request(
self,
http_method: Callable,
path: str,
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str]]] = None,
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str], str]] = None,
content_type: Optional[str] = None,
) -> Any:
if content_type:
self.headers['Content-Type'] = content_type
try:
request_path = self.config.url + '/' + path
request = http_method(
request_path,
timeout=self.config.timeout,
headers=self.headers,
data=json.dumps(body) if body else "null"
)
if isinstance(body, bytes):
request = http_method(
request_path,
timeout=self.config.timeout,
headers=self.headers,
data=body
)
else:
request = http_method(
request_path,
timeout=self.config.timeout,
headers=self.headers,
data=json.dumps(body) if body else "null"
)
return self.__validate(request)

except requests.exceptions.Timeout as err:
Expand All @@ -45,16 +55,18 @@ def get(
def post(
self,
path: str,
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str]]] = None,
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str], str]] = None,
content_type: Optional[str] = 'application/json',
) -> Any:
return self.send_request(requests.post, path, body)
return self.send_request(requests.post, path, body, content_type)

def put(
self,
path: str,
body: Optional[Union[Dict[str, Any], List[Dict[str, Any]], List[str]]] = None,
content_type: Optional[str] = 'application/json',
) -> Any:
return self.send_request(requests.put, path, body)
return self.send_request(requests.put, path, body, content_type)

def delete(
self,
Expand Down
134 changes: 123 additions & 11 deletions meilisearch/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ def get_documents(self, parameters: Optional[Dict[str, Any]] = None) -> List[Dic
"""
if parameters is None:
parameters = {}

return self.http.get(
f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}?{urllib.parse.urlencode(parameters)}'
)
Expand Down Expand Up @@ -346,11 +345,7 @@ def add_documents(
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
"""
if primary_key is None:
url = f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}'
else:
primary_key = urllib.parse.urlencode({'primaryKey': primary_key})
url = f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}?{primary_key}'
url = self._build_url(primary_key)
return self.http.post(url, documents)

def add_documents_in_batches(
Expand Down Expand Up @@ -391,6 +386,118 @@ def add_documents_in_batches(

return update_ids

def add_documents_json(
self,
str_documents: str,
primary_key: Optional[str] = None,
) -> Dict[str, int]:
"""Add string documents from JSON file to the index.
Parameters
----------
str_documents:
String of document from a JSON file.
primary_key (optional):
The primary-key used in index. Ignored if already set up.
Returns
-------
update:
Dictionary containing an update id to track the action:
https://docs.meilisearch.com/reference/api/updates.html#get-an-update-status
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.add_documents_raw(str_documents, primary_key, 'application/json')

def add_documents_csv(
self,
str_documents: str,
primary_key: Optional[str] = None,
) -> Dict[str, int]:
"""Add string documents from a CSV file to the index.
Parameters
----------
str_documents:
String of document from a CSV file.
primary_key (optional):
The primary-key used in index. Ignored if already set up.
Returns
-------
update:
Dictionary containing an update id to track the action:
https://docs.meilisearch.com/reference/api/updates.html#get-an-update-status
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.add_documents_raw(str_documents, primary_key, 'text/csv')

def add_documents_ndjson(
self,
str_documents: str,
primary_key: Optional[str] = None,
) -> Dict[str, int]:
"""Add string documents from a NDJSON file to the index.
Parameters
----------
str_documents:
String of document from a NDJSON file.
primary_key (optional):
The primary-key used in index. Ignored if already set up.
Returns
-------
update:
Dictionary containing an update id to track the action:
https://docs.meilisearch.com/reference/api/updates.html#get-an-update-status
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.add_documents_raw(str_documents, primary_key, 'application/x-ndjson')

def add_documents_raw(
self,
str_documents: str,
primary_key: Optional[str] = None,
content_type: Optional[str] = None,
) -> Dict[str, int]:
"""Add string documents to the index.
Parameters
----------
str_documents:
String of document.
primary_key (optional):
The primary-key used in index. Ignored if already set up.
type:
The type of document. Type available: 'csv', 'json', 'jsonl'
Returns
-------
update:
Dictionary containing an update id to track the action:
https://docs.meilisearch.com/reference/api/updates.html#get-an-update-status
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
"""
url = self._build_url(primary_key)
return self.http.post(url, str_documents, content_type)

def update_documents(
self,
documents: List[Dict[str, Any]],
Expand All @@ -416,11 +523,7 @@ def update_documents(
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
"""
if primary_key is None:
url = f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}'
else:
primary_key = urllib.parse.urlencode({'primaryKey': primary_key})
url = f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}?{primary_key}'
url = self._build_url(primary_key)
return self.http.put(url, documents)

def update_documents_in_batches(
Expand Down Expand Up @@ -1134,3 +1237,12 @@ def _iso_to_date_time(iso_date: Optional[Union[datetime, str]]) -> Optional[date

def __settings_url_for(self, sub_route: str) -> str:
return f'{self.config.paths.index}/{self.uid}/{self.config.paths.setting}/{sub_route}'

def _build_url(
self,
primary_key: Optional[str] = None,
) -> str:
if primary_key is None:
return f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}'
primary_key = urllib.parse.urlencode({'primaryKey': primary_key})
return f'{self.config.paths.index}/{self.uid}/{self.config.paths.document}?{primary_key}'
26 changes: 25 additions & 1 deletion meilisearch/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,33 @@ def small_movies():
"""
Runs once per session. Provides the content of small_movies.json.
"""
with open('./datasets/small_movies.json', 'r', encoding="utf8") as movie_file:
with open('./datasets/small_movies.json', 'r', encoding='utf-8') as movie_file:
yield json.loads(movie_file.read())

@fixture(scope='session')
def small_movies_json_file():
"""
Runs once per session. Provides the content of small_movies.json from read.
"""
with open('./datasets/small_movies.json', 'r', encoding='utf-8') as movie_json_file:
return movie_json_file.read().encode('utf-8')

@fixture(scope='session')
def songs_csv():
"""
Runs once per session. Provides the content of songs.csv from read..
"""
with open('./datasets/songs.csv', 'r', encoding='utf-8') as song_csv_file:
return song_csv_file.read().encode('utf-8')

@fixture(scope='session')
def songs_ndjson():
"""
Runs once per session. Provides the content of songs.ndjson from read..
"""
with open('./datasets/songs.ndjson', 'r', encoding='utf-8') as song_ndjson_file:
return song_ndjson_file.read().encode('utf-8')

@fixture(scope='function')
def empty_index(client):
def index_maker(index_name=common.INDEX_UID):
Expand Down
33 changes: 33 additions & 0 deletions meilisearch/tests/index/test_index_document_meilisearch.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,36 @@ def test_delete_all_documents(index_with_documents):
response = index.get_documents()
assert isinstance(response, list)
assert response == []

def test_add_documents_csv(empty_index, songs_csv):
"""Tests adding new documents to a clean index."""
index = empty_index()
response = index.add_documents_csv(songs_csv)
assert isinstance(response, dict)
assert 'updateId' in response
update = index.wait_for_pending_update(response['updateId'])
assert update['status'] == 'processed'
assert update['type']['number'] != 0
assert index.get_primary_key() == 'id'

def test_add_documents_json(empty_index, small_movies_json_file):
"""Tests adding new documents to a clean index."""
index = empty_index()
response = index.add_documents_json(small_movies_json_file)
assert isinstance(response, dict)
assert 'updateId' in response
update = index.wait_for_pending_update(response['updateId'])
assert update['status'] == 'processed'
assert update['type']['number'] != 0
assert index.get_primary_key() == 'id'

def test_add_documents_ndjson(empty_index, songs_ndjson):
"""Tests adding new documents to a clean index."""
index = empty_index()
response = index.add_documents_ndjson(songs_ndjson)
assert isinstance(response, dict)
assert 'updateId' in response
update = index.wait_for_pending_update(response['updateId'])
assert update['status'] == 'processed'
assert update['type']['number'] != 0
assert index.get_primary_key() == 'id'

0 comments on commit df74e09

Please sign in to comment.