Skip to content

Commit 04b4518

Browse files
bors[bot]meili-botalallema
authored
Merge #709 #726
709: Changes related to the next Meilisearch release (v1.1.0) r=bidoubiwa a=meili-bot Related to this issue: meilisearch/integration-guides#251 This PR: - gathers the changes related to the next Meilisearch release (v1.1.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 v1.1.0 is out. ⚠️ This PR should NOT be merged until the next release of Meilisearch (v1.1.0) is out. _This PR is auto-generated for the [pre-release week](https://github.com/meilisearch/integration-guides/blob/main/resources/pre-release-week.md) purpose._ Done: - #714 - #716 726: Update version for the next release (v0.26.0) r=bidoubiwa a=meili-bot This version makes this package compatible with Meilisearch v1.1 🎉 Check out the changelog of [Meilisearch v1.1](https://github.com/meilisearch/meilisearch/releases/tag/v1.1.0) for more information on the changes. ## ⚠️ Breaking changes * Change error names from MeiliSerach to Meilisearch (#720) `@sanders41` ## 🚀 Enhancements - Add the ability to provide a specific `csv-delimiter` when adding and updating documents in CSV format (#716) `@alallema` - New method `client.multi_search()` provides the possibility to make multiple requests at once (#714) `@alallema` Example: ```python client.multi_search( [ {'indexUid': 'movies', 'q': 'pooh', 'limit': 5}, {'indexUid': 'movies', 'q': 'nemo', 'limit': 5}, {'indexUid': 'movie_ratings', 'q': 'us'} ] ) ``` Thanks again to `@alallema` and `@sanders41!` 🎉 Co-authored-by: meili-bot <[email protected]> Co-authored-by: alallema <[email protected]> Co-authored-by: Amélie <[email protected]>
3 parents 9e5e00d + b4e6c01 + 8cf69f7 commit 04b4518

9 files changed

+152
-14
lines changed

datasets/songs_custom_delimiter.csv

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
id;title;album;artist;genre;country;released;duration;released-timestamp;duration-float
2+
702481615;Armatage Shanks;Dookie: The Ultimate Critical Review;Green Day;Rock;Europe;2005;;1104537600;
3+
888221515;Old Folks;Six Classic Albums Plus Bonus Tracks;Harold Land;Jazz;Europe;2013;6:36;1356998400;6.36
4+
1382413601;คำขอร้อง;สำเนียงคนจันทร์ / เอาเถอะถ้าเห็นเขาดีกว่า;อิทธิพล บำรุงกุล;"Folk; World; & Country";Thailand;;;;
5+
190889300;Track 1;Summer Breeze;Dreas;Funk / Soul;US;2008;18:56;1199145600;18.56
6+
813645611;Slave (Alternative Version);Honky Château;Elton John;Rock;Europe;;2:53;;2.5300000000000002
7+
394018506;Sex & Geld;Trackz Für Den Index;Mafia Clikk;Hip Hop;Germany;2006;5:02;1136073600;5.02
8+
1522481803;Pisciaunella;Don Pepp U Pacce;Giovanni Russo (2);"Folk; World; & Country";Italy;1980;;315532800;
9+
862296713;不知;Kiss 2001 Hong Kong Live Concert;Various;Electronic;Hong Kong;2002-04-13;;1018656000;
10+
467946423;Rot;Be Quick Or Be Dead Vol. 3;Various;Electronic;Serbia;2013-06-20;1:00;1371686400;1
11+
1323854803;"Simulation Project 1; ツキハナ「Moonflower」";Unlimited Dream Company;Amun Dragoon;Electronic;US;2018-04-10;2:44;1523318400;2.44
12+
235115704;Doctor Vine;The Big F;The Big F;Rock;US;1989;5:29;599616000;5.29
13+
249025232;"Ringel; Ringel; Reihe";Kinderlieder ABC - Der Bielefelder Kinderchor Singt 42 Lieder Von A-Z;Der Bielefelder Kinderchor;Children's;Germany;1971;;31536000;
14+
710094000;Happy Safari = Safari Feliz;Safari Swings Again = El Safari Sigue En Su Swing;Bert Kaempfert & His Orchestra;Jazz;Argentina;1977;2:45;220924800;2.45
15+
538632700;Take Me Up;Spring;Various;Electronic;US;2000;3:06;946684800;3.06
16+
1556505508;Doin To Me ( Radio Version );Say My Name;Netta Dogg;Hip Hop;US;2005;;1104537600;
17+
1067031900;Concerto For Balloon & Orchestra / Concerto For Synthesizer & Orchestra;Concerto For Balloon & Orchestra And Three Overtures;Stanyan String & Wind Ensemble;Classical;US;1977;;220924800;
18+
137251914;"I Love The Nightlife (Disco 'Round) (Real Rapino 7"" Mix)";The Adventures Of Priscilla: Queen Of The Desert - Original Motion Picture Soundtrack;Various;Stage & Screen;US;1994;3:31;757382400;3.31
19+
554983904;Walking On The Moon;Certifiable (Live In Buenos Aires);The Police;Rock;Malaysia;2008-11-00;;1225497600;
20+
557616002;Two Soldiers;Jerry Garcia / David Grisman;David Grisman;"Folk; World; & Country";US;2014-04-00;4:24;1396310400;4.24
21+
878936809;When You Gonna Learn;Live At Firenze 93;Jamiroquai;Funk / Soul;France;2004;13:01;1072915200;13.01

meilisearch/client.py

+26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# pylint: disable=too-many-public-methods
2+
13
from __future__ import annotations
24

35
import base64
@@ -205,6 +207,30 @@ def index(self, uid: str) -> Index:
205207
return Index(self.config, uid=uid)
206208
raise ValueError("The index UID should not be None")
207209

210+
def multi_search(self, queries: List[Dict[str, Any]]) -> Dict[str, List[Dict[str, Any]]]:
211+
"""Multi-index search.
212+
213+
Parameters
214+
----------
215+
queries:
216+
List of dictionaries containing the specified indexes and their search queries
217+
https://docs.meilisearch.com/reference/api/search.html#search-in-an-index
218+
219+
Returns
220+
-------
221+
results:
222+
Dictionary of results for each search query
223+
224+
Raises
225+
------
226+
MeilisearchApiError
227+
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
228+
"""
229+
return self.http.post(
230+
f"{self.config.paths.multi_search}",
231+
body={"queries": queries},
232+
)
233+
208234
def get_all_stats(self) -> Dict[str, Any]:
209235
"""Get all stats of Meilisearch
210236

meilisearch/config.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class Paths:
1616
task = "tasks"
1717
stat = "stats"
1818
search = "search"
19+
multi_search = "multi-search"
1920
document = "documents"
2021
setting = "settings"
2122
ranking_rules = "ranking-rules"

meilisearch/index.py

+28-9
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ def add_documents_csv(
434434
self,
435435
str_documents: str,
436436
primary_key: Optional[str] = None,
437+
csv_delimiter: Optional[str] = None,
437438
) -> TaskInfo:
438439
"""Add string documents from a CSV file to the index.
439440
@@ -443,6 +444,8 @@ def add_documents_csv(
443444
String of document from a CSV file.
444445
primary_key (optional):
445446
The primary-key used in index. Ignored if already set up.
447+
csv_delimiter:
448+
One ASCII character used to customize the delimiter for CSV. Comma used by default.
446449
447450
Returns
448451
-------
@@ -455,7 +458,7 @@ def add_documents_csv(
455458
MeilisearchApiError
456459
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
457460
"""
458-
return self.add_documents_raw(str_documents, primary_key, "text/csv")
461+
return self.add_documents_raw(str_documents, primary_key, "text/csv", csv_delimiter)
459462

460463
def add_documents_ndjson(
461464
self,
@@ -489,6 +492,7 @@ def add_documents_raw(
489492
str_documents: str,
490493
primary_key: Optional[str] = None,
491494
content_type: Optional[str] = None,
495+
csv_delimiter: Optional[str] = None,
492496
) -> TaskInfo:
493497
"""Add string documents to the index.
494498
@@ -499,7 +503,10 @@ def add_documents_raw(
499503
primary_key (optional):
500504
The primary-key used in index. Ignored if already set up.
501505
type:
502-
The type of document. Type available: 'csv', 'json', 'jsonl'
506+
The type of document. Type available: 'csv', 'json', 'jsonl'.
507+
csv_delimiter:
508+
One ASCII character used to customize the delimiter for CSV.
509+
Note: The csv delimiter can only be used with the Content-Type text/csv.
503510
504511
Returns
505512
-------
@@ -512,7 +519,7 @@ def add_documents_raw(
512519
MeilisearchApiError
513520
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
514521
"""
515-
url = self._build_url(primary_key)
522+
url = self._build_url(primary_key=primary_key, csv_delimiter=csv_delimiter)
516523
response = self.http.post(url, str_documents, content_type)
517524
return TaskInfo(**response)
518525

@@ -601,6 +608,7 @@ def update_documents_csv(
601608
self,
602609
str_documents: str,
603610
primary_key: Optional[str] = None,
611+
csv_delimiter: Optional[str] = None,
604612
) -> TaskInfo:
605613
"""Update documents as a csv string in the index.
606614
@@ -609,7 +617,9 @@ def update_documents_csv(
609617
str_documents:
610618
String of document from a CSV file.
611619
primary_key (optional):
612-
The primary-key used in index. Ignored if already set up
620+
The primary-key used in index. Ignored if already set up.
621+
csv_delimiter:
622+
One ASCII character used to customize the delimiter for CSV. Comma used by default.
613623
614624
Returns
615625
-------
@@ -622,13 +632,14 @@ def update_documents_csv(
622632
MeilisearchApiError
623633
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
624634
"""
625-
return self.update_documents_raw(str_documents, primary_key, "text/csv")
635+
return self.update_documents_raw(str_documents, primary_key, "text/csv", csv_delimiter)
626636

627637
def update_documents_raw(
628638
self,
629639
str_documents: str,
630640
primary_key: Optional[str] = None,
631641
content_type: Optional[str] = None,
642+
csv_delimiter: Optional[str] = None,
632643
) -> TaskInfo:
633644
"""Update documents as a string in the index.
634645
@@ -640,6 +651,9 @@ def update_documents_raw(
640651
The primary-key used in index. Ignored if already set up.
641652
type:
642653
The type of document. Type available: 'csv', 'json', 'jsonl'
654+
csv_delimiter:
655+
One ASCII character used to customize the delimiter for CSV.
656+
Note: The csv delimiter can only be used with the Content-Type text/csv.
643657
644658
Returns
645659
-------
@@ -652,7 +666,7 @@ def update_documents_raw(
652666
MeilisearchApiError
653667
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
654668
"""
655-
url = self._build_url(primary_key)
669+
url = self._build_url(primary_key=primary_key, csv_delimiter=csv_delimiter)
656670
response = self.http.put(url, str_documents, content_type)
657671
return TaskInfo(**response)
658672

@@ -1530,8 +1544,13 @@ def __settings_url_for(self, sub_route: str) -> str:
15301544
def _build_url(
15311545
self,
15321546
primary_key: Optional[str] = None,
1547+
csv_delimiter: Optional[str] = None,
15331548
) -> str:
1534-
if primary_key is None:
1549+
parameters = {}
1550+
if primary_key:
1551+
parameters["primaryKey"] = primary_key
1552+
if csv_delimiter:
1553+
parameters["csvDelimiter"] = csv_delimiter
1554+
if primary_key is None and csv_delimiter is None:
15351555
return f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}"
1536-
primary_key = parse.urlencode({"primaryKey": primary_key})
1537-
return f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}?{primary_key}"
1556+
return f"{self.config.paths.index}/{self.uid}/{self.config.paths.document}?{parse.urlencode(parameters)}"

meilisearch/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
__version__ = "0.25.0"
3+
__version__ = "0.26.0"
44

55

66
def qualified_version() -> str:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import pytest
2+
3+
from meilisearch.errors import MeilisearchApiError
4+
5+
6+
def test_basic_multi_search(client, empty_index):
7+
"""Tests multi-search on two indexes."""
8+
empty_index("indexA")
9+
empty_index("indexB")
10+
response = client.multi_search(
11+
[{"indexUid": "indexA", "q": ""}, {"indexUid": "indexB", "q": ""}]
12+
)
13+
14+
assert isinstance(response, dict)
15+
assert response["results"][0]["indexUid"] == "indexA"
16+
assert response["results"][1]["indexUid"] == "indexB"
17+
assert response["results"][0]["limit"] == 20
18+
assert response["results"][1]["limit"] == 20
19+
20+
21+
def test_multi_search_one_index(client, empty_index):
22+
"""Tests multi-search on a simple query."""
23+
empty_index("indexA")
24+
response = client.multi_search([{"indexUid": "indexA", "q": ""}])
25+
26+
assert isinstance(response, dict)
27+
assert response["results"][0]["indexUid"] == "indexA"
28+
assert response["results"][0]["limit"] == 20
29+
30+
31+
def test_multi_search_on_no_index(client):
32+
"""Tests multi-search on a non existing index."""
33+
with pytest.raises(MeilisearchApiError):
34+
client.multi_search([{"indexUid": "indexDoesNotExist", "q": ""}])

tests/conftest.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,25 @@ def small_movies_json_file():
7070
@fixture(scope="session")
7171
def songs_csv():
7272
"""
73-
Runs once per session. Provides the content of songs.csv from read..
73+
Runs once per session. Provides the content of songs.csv from read.
7474
"""
7575
with open("./datasets/songs.csv", encoding="utf-8") as song_csv_file:
7676
return song_csv_file.read().encode("utf-8")
7777

7878

79+
@fixture(scope="session")
80+
def songs_csv_custom_separator():
81+
"""
82+
Runs once per session. Provides the content of songs_custom_delimiter.csv from read.
83+
"""
84+
with open("./datasets/songs_custom_delimiter.csv", encoding="utf-8") as song_csv_file:
85+
return song_csv_file.read().encode("utf-8")
86+
87+
7988
@fixture(scope="session")
8089
def songs_ndjson():
8190
"""
82-
Runs once per session. Provides the content of songs.ndjson from read..
91+
Runs once per session. Provides the content of songs.ndjson from read.
8392
"""
8493
with open("./datasets/songs.ndjson", encoding="utf-8") as song_ndjson_file:
8594
return song_ndjson_file.read().encode("utf-8")

tests/index/test_index_document_meilisearch.py

+28
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,20 @@ def test_add_documents_csv(empty_index, songs_csv):
197197
assert index.get_primary_key() == "id"
198198

199199

200+
def test_add_documents_csv_with_delimiter(empty_index, songs_csv_custom_separator):
201+
"""Tests adding new documents to a clean index."""
202+
index = empty_index("csv-delimiter")
203+
response = index.add_documents_csv(songs_csv_custom_separator, csv_delimiter=";")
204+
assert isinstance(response, TaskInfo)
205+
assert response.task_uid is not None
206+
task = index.wait_for_task(response.task_uid)
207+
assert task.status == "succeeded"
208+
assert task.details["receivedDocuments"] == 20
209+
documents = index.get_documents().results
210+
assert documents[1].country == "Europe"
211+
assert documents[4].artist == "Elton John"
212+
213+
200214
def test_update_documents_csv(index_with_documents, songs_csv):
201215
"""Tests updating a single document with csv string."""
202216
index = index_with_documents()
@@ -208,6 +222,20 @@ def test_update_documents_csv(index_with_documents, songs_csv):
208222
assert index.get_primary_key() == "id"
209223

210224

225+
def test_update_documents_csv_with_delimiter(index_with_documents, songs_csv_custom_separator):
226+
"""Tests adding new documents to a clean index."""
227+
index = index_with_documents()
228+
response = index.update_documents_csv(songs_csv_custom_separator, csv_delimiter=";")
229+
assert isinstance(response, TaskInfo)
230+
assert response.task_uid is not None
231+
task = index.wait_for_task(response.task_uid)
232+
assert task.status == "succeeded"
233+
assert task.details["receivedDocuments"] == 20
234+
document = index.get_document("813645611")
235+
assert document.country == "Europe"
236+
assert document.artist == "Elton John"
237+
238+
211239
def test_add_documents_json(empty_index, small_movies_json_file):
212240
"""Tests adding new documents to a clean index."""
213241
index = empty_index()

tests/index/test_index_search_meilisearch.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
def test_basic_search(index_with_documents):
5-
"""Tests search with an simple query."""
5+
"""Tests search with a simple query."""
66
response = index_with_documents().search("How to Train Your Dragon")
77
assert isinstance(response, dict)
88
assert response["hits"][0]["id"] == "166428"
@@ -356,7 +356,7 @@ def test_phrase_search(index_with_documents):
356356

357357

358358
def test_basic_search_on_nested_documents(index_with_documents, nested_movies):
359-
"""Tests search with an simple query on nested fields."""
359+
"""Tests search with a simple query on nested fields."""
360360
response = index_with_documents("nested_fields_index", nested_movies).search("An awesome")
361361
assert isinstance(response, dict)
362362
assert response["hits"][0]["id"] == 5

0 commit comments

Comments
 (0)