2023-02-06 13:46:12 +01:00
use meili_snap ::{ json_string , snapshot } ;
2024-07-04 12:40:02 +02:00
use super ::{ DOCUMENTS , FRUITS_DOCUMENTS , NESTED_DOCUMENTS } ;
2023-02-06 13:46:12 +01:00
use crate ::common ::Server ;
2023-09-11 16:50:53 +02:00
use crate ::json ;
2024-07-04 12:40:02 +02:00
use crate ::search ::{ SCORE_DOCUMENTS , VECTOR_DOCUMENTS } ;
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_empty_list ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server . multi_search ( json! ( { " queries " : [ ] } ) ) . await ;
snapshot! ( code , @ " 200 OK " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" results " : [ ]
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_empty_list ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server . multi_search ( json! ( { " federation " : { } , " queries " : [ ] } ) ) . await ;
snapshot! ( code , @ " 200 OK " ) ;
snapshot! ( json_string! ( response , { " .processingTimeMs " = > " [time] " } ) , @ r ###"
{
" hits " : [ ] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 0
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_json_object ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server . multi_search ( json! ( { } ) ) . await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Missing field `queries` " ,
" code " : " bad_request " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#bad_request "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_no_queries ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server . multi_search ( json! ( { " federation " : { } } ) ) . await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Missing field `queries` " ,
" code " : " bad_request " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#bad_request "
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_json_array ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server . multi_search ( json! ( [ ] ) ) . await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Invalid value type: expected an object, but found an array: `[]` " ,
" code " : " bad_request " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#bad_request "
}
" ###);
}
#[ actix_rt::test ]
async fn simple_search_single_index ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " test " , " q " : " captain " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
2023-06-07 10:45:25 +02:00
insta ::assert_json_snapshot! ( response [ " results " ] , { " [].processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
2023-02-06 13:46:12 +01:00
[
{
" indexUid " : " test " ,
" hits " : [
{
2023-03-29 14:36:17 +02:00
" title " : " Gläss " ,
2023-12-13 21:49:13 +01:00
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
2024-05-14 14:14:02 +02:00
- 100.0 ,
340.0 ,
90.0
2023-12-13 21:49:13 +01:00
]
}
2023-02-06 13:46:12 +01:00
}
] ,
" query " : " glass " ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 1
} ,
{
" indexUid " : " test " ,
" hits " : [
{
" title " : " Captain Marvel " ,
2023-12-13 21:49:13 +01:00
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
2024-05-14 14:14:02 +02:00
1.0 ,
2.0 ,
54.0
2023-12-13 21:49:13 +01:00
]
}
2023-02-06 13:46:12 +01:00
}
] ,
" query " : " captain " ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 1
}
]
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_single_search_single_index ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 1
}
" ###);
}
#[ actix_rt::test ]
async fn federation_multiple_search_single_index ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " the bat " } ,
{ " indexUid " : " test " , " q " : " badman returns " } ,
{ " indexUid " : " test " , " q " : " batman " } ,
{ " indexUid " : " test " , " q " : " batman returns " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 3 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.5
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 5
}
" ###);
}
#[ actix_rt::test ]
async fn federation_two_search_single_index ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " test " , " q " : " captain " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 2
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn simple_search_missing_index_uid ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " q " : " glass " } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , @ r ###"
{
" message " : " Missing field `indexUid` inside `.queries[0]` " ,
" code " : " missing_index_uid " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#missing_index_uid "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_simple_search_missing_index_uid ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " q " : " glass " } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , @ r ###"
{
" message " : " Missing field `indexUid` inside `.queries[0]` " ,
" code " : " missing_index_uid " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#missing_index_uid "
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn simple_search_illegal_index_uid ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " hé " , " q " : " glass " } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , @ r ###"
{
" message " : " Invalid value at `.queries[0].indexUid`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). " ,
" code " : " invalid_index_uid " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_index_uid "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_search_illegal_index_uid ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " hé " , " q " : " glass " } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , @ r ###"
{
" message " : " Invalid value at `.queries[0].indexUid`: `hé` is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_). " ,
" code " : " invalid_index_uid " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_index_uid "
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn simple_search_two_indexes ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
2023-03-29 14:36:17 +02:00
{ " indexUid " : " nested " , " q " : " pésti " } ,
2023-02-06 13:46:12 +01:00
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
2023-06-07 10:45:25 +02:00
insta ::assert_json_snapshot! ( response [ " results " ] , { " [].processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
2023-02-06 13:46:12 +01:00
[
{
" indexUid " : " test " ,
" hits " : [
{
2023-03-29 14:36:17 +02:00
" title " : " Gläss " ,
2023-12-13 21:49:13 +01:00
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
2024-05-14 14:14:02 +02:00
- 100.0 ,
340.0 ,
90.0
2023-12-13 21:49:13 +01:00
]
}
2023-02-06 13:46:12 +01:00
}
] ,
" query " : " glass " ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 1
} ,
{
" indexUid " : " nested " ,
" hits " : [
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
2023-12-13 21:49:13 +01:00
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
2024-05-14 14:14:02 +02:00
1.0 ,
2.0 ,
3.0
2023-12-13 21:49:13 +01:00
]
}
2023-02-06 13:46:12 +01:00
} ,
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
2023-12-13 21:49:13 +01:00
] ,
" _vectors " : {
" manual " : [
2024-05-14 14:14:02 +02:00
1.0 ,
2.0 ,
54.0
2023-12-13 21:49:13 +01:00
]
}
2023-02-06 13:46:12 +01:00
}
] ,
2023-03-29 14:36:17 +02:00
" query " : " pésti " ,
2023-02-06 13:46:12 +01:00
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 2
}
]
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_two_search_two_indexes ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
} ,
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
}
} ,
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.7803030303030303
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 3
}
" ###);
}
#[ actix_rt::test ]
async fn federation_multiple_search_multiple_indexes ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let index = server . index ( " score " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 2 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " test " , " q " : " captain " } ,
{ " indexUid " : " nested " , " q " : " pésti " } ,
{ " indexUid " : " test " , " q " : " Escape " } ,
{ " indexUid " : " nested " , " q " : " jean " } ,
{ " indexUid " : " score " , " q " : " jean " } ,
{ " indexUid " : " test " , " q " : " the bat " } ,
{ " indexUid " : " score " , " q " : " the bat " } ,
{ " indexUid " : " score " , " q " : " badman returns " } ,
{ " indexUid " : " score " , " q " : " batman " } ,
{ " indexUid " : " score " , " q " : " batman returns " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
} ,
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 10 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Escape Room " ,
" id " : " 522681 " ,
" _vectors " : {
" manual " : [
10.0 ,
- 23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 3 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 951 ,
" father " : " jean-baptiste " ,
" mother " : " sophie " ,
" doggos " : [
{
" name " : " turbo " ,
" age " : 5
} ,
{
" name " : " fast " ,
" age " : 6
}
] ,
" cattos " : [
" moumoute " ,
" gomez "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 4 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.7803030303030303
}
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 8 ,
" weightedRankingScore " : 0.5
}
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
231.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 6 ,
" weightedRankingScore " : 0.4166666666666667
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 12
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_one_index_doesnt_exist ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
2023-03-29 14:36:17 +02:00
{ " indexUid " : " nested " , " q " : " pésti " } ,
2023-02-06 13:46:12 +01:00
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Index `nested` not found. " ,
" code " : " index_not_found " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#index_not_found "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_one_index_doesnt_exist ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Index `nested` not found. " ,
" code " : " index_not_found " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#index_not_found "
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_multiple_indexes_dont_exist ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
2023-03-29 14:36:17 +02:00
{ " indexUid " : " nested " , " q " : " pésti " } ,
2023-02-06 13:46:12 +01:00
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[0]`: Index `test` not found. " ,
" code " : " index_not_found " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#index_not_found "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_multiple_indexes_dont_exist ( ) {
let server = Server ::new ( ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
// order of indexes that are not found depends on the alphabetical order of index names
// the query index is the lowest index with that index
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Index `nested` not found. " ,
" code " : " index_not_found " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#index_not_found "
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_one_query_error ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " facets " : [ " title " ] } ,
2023-03-29 14:36:17 +02:00
{ " indexUid " : " nested " , " q " : " pésti " } ,
2023-02-06 13:46:12 +01:00
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[0]`: Invalid facet distribution, this index does not have configured filterable attributes. " ,
" code " : " invalid_search_facets " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_facets "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_one_query_error ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " , " filter " : [ " title = toto " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Attribute `title` is not filterable. This index does not have configured filterable attributes. \n 1:6 title = toto " ,
" code " : " invalid_search_filter " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_filter "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_one_query_sort_error ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " doggos:desc " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Attribute `doggos` is not sortable. This index does not have configured sortable attributes. " ,
" code " : " invalid_search_sort " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_sort "
}
" ###);
}
2023-02-06 13:46:12 +01:00
#[ actix_rt::test ]
async fn search_multiple_query_errors ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " facets " : [ " title " ] } ,
2023-03-29 14:36:17 +02:00
{ " indexUid " : " nested " , " q " : " pésti " , " facets " : [ " doggos " ] } ,
2023-02-06 13:46:12 +01:00
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[0]`: Invalid facet distribution, this index does not have configured filterable attributes. " ,
" code " : " invalid_search_facets " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_facets "
}
" ###);
}
2024-07-04 12:40:02 +02:00
#[ actix_rt::test ]
async fn federation_multiple_query_errors ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " filter " : [ " title = toto " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " filter " : [ " doggos IN [intel, kefir] " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[0]`: Attribute `title` is not filterable. This index does not have configured filterable attributes. \n 1:6 title = toto " ,
" code " : " invalid_search_filter " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_filter "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_multiple_query_sort_errors ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " sort " : [ " title:desc " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " doggos:desc " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[0]`: Attribute `title` is not sortable. This index does not have configured sortable attributes. " ,
" code " : " invalid_search_sort " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_sort "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_multiple_query_errors_interleaved ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " , " filter " : [ " doggos IN [intel, kefir] " ] } ,
{ " indexUid " : " test " , " q " : " glass " , " filter " : [ " title = toto " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Attribute `doggos` is not filterable. This index does not have configured filterable attributes. \n 1:7 doggos IN [intel, kefir] " ,
" code " : " invalid_search_filter " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_filter "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_multiple_query_sort_errors_interleaved ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " test " , " q " : " glass " } ,
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " doggos:desc " ] } ,
{ " indexUid " : " test " , " q " : " glass " , " sort " : [ " title:desc " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
snapshot! ( json_string! ( response ) , @ r ###"
{
" message " : " Inside `.queries[1]`: Attribute `doggos` is not sortable. This index does not have configured sortable attributes. " ,
" code " : " invalid_search_sort " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_search_sort "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_filter ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " fruits " ) ;
let documents = FRUITS_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings (
json! ( { " searchableAttributes " : [ " name " ] , " filterableAttributes " : [ " BOOST " ] } ) ,
)
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " , " filter " : " BOOST = true " , " showRankingScore " : true , " federationOptions " : { " weight " : 3.0 } } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" name " : " Exclusive sale: Red delicious apple " ,
" id " : " red-delicious-boosted " ,
" BOOST " : true ,
" _federation " : {
" indexUid " : " fruits " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 2.7281746031746033
} ,
" _rankingScore " : 0.9093915343915344
} ,
{
" name " : " Exclusive sale: green apple " ,
" id " : " green-apple-boosted " ,
" BOOST " : true ,
" _federation " : {
" indexUid " : " fruits " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.318181818181818
} ,
" _rankingScore " : 0.4393939393939394
} ,
{
" name " : " Red apple gala " ,
" id " : " red-apple-gala " ,
" _federation " : {
" indexUid " : " fruits " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.953042328042328
} ,
" _rankingScore " : 0.953042328042328
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 3
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_same_indexes_same_criterion_same_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " mother " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// two identical placeholder search should have all results from first query
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 750 ,
" father " : " romain " ,
" mother " : " michelle " ,
" cattos " : [
" enigma "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 951 ,
" father " : " jean-baptiste " ,
" mother " : " sophie " ,
" doggos " : [
{
" name " : " turbo " ,
" age " : 5
} ,
{
" name " : " fast " ,
" age " : 6
}
] ,
" cattos " : [
" moumoute " ,
" gomez "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 4
}
" ###);
// mix and match query
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " jean " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.7803030303030303
} ,
" _rankingScore " : 0.7803030303030303
} ,
{
" id " : 951 ,
" father " : " jean-baptiste " ,
" mother " : " sophie " ,
" doggos " : [
{
" name " : " turbo " ,
" age " : 5
} ,
{
" name " : " fast " ,
" age " : 6
}
] ,
" cattos " : [
" moumoute " ,
" gomez "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _rankingScore " : 0.9848484848484848
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 3
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_same_indexes_same_criterion_opposite_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " mother " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// two identical placeholder search should have all results from first query
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " mother:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #0 and #1 are incompatible: \n 1. `queries[0].sort[0]`, `nested.rankingRules[0]`: ascending sort rule(s) on field `mother` \n 2. `queries[1].sort[0]`, `nested.rankingRules[0]`: descending sort rule(s) on field `mother` \n - cannot compare two sort rules in opposite directions \n - note: The ranking rules of query #0 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n - note: The ranking rules of query #1 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
// mix and match query: should be ranked by ranking score
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " jean " , " sort " : [ " mother:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #0 and #1 are incompatible: \n 1. `queries[0].sort[0]`, `nested.rankingRules[0]`: ascending sort rule(s) on field `mother` \n 2. `queries[1].sort[0]`, `nested.rankingRules[0]`: descending sort rule(s) on field `mother` \n - cannot compare two sort rules in opposite directions \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_same_indexes_different_criterion_same_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " mother " , " father " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// return mothers and fathers ordered accross fields.
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " father:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 951 ,
" father " : " jean-baptiste " ,
" mother " : " sophie " ,
" doggos " : [
{
" name " : " turbo " ,
" age " : 5
} ,
{
" name " : " fast " ,
" age " : 6
}
] ,
" cattos " : [
" moumoute " ,
" gomez "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 750 ,
" father " : " romain " ,
" mother " : " michelle " ,
" cattos " : [
" enigma "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 4
}
" ###);
// mix and match query: will be sorted across mother and father names
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " mother:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " jean-bap " , " sort " : [ " father:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " jea " , " sort " : [ " father:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" id " : 654 ,
" father " : " pierre " ,
" mother " : " sabine " ,
" doggos " : [
{
" name " : " gros bill " ,
" age " : 8
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.7803030303030303
} ,
" _rankingScore " : 0.7803030303030303
} ,
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2
} ,
{
" name " : " buddy " ,
" age " : 4
}
] ,
" cattos " : " pésti " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" id " : 951 ,
" father " : " jean-baptiste " ,
" mother " : " sophie " ,
" doggos " : [
{
" name " : " turbo " ,
" age " : 5
} ,
{
" name " : " fast " ,
" age " : 6
}
] ,
" cattos " : [
" moumoute " ,
" gomez "
] ,
" _vectors " : {
" manual " : [
10.0 ,
23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9991181657848324
} ,
" _rankingScore " : 0.9991181657848324
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 3
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_same_indexes_different_criterion_opposite_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " mother " , " father " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// two identical placeholder search should have all results from first query
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " " , " sort " : [ " father:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #0 and #1 are incompatible: \n 1. `queries[0].sort[0]`, `nested.rankingRules[0]`: ascending sort rule(s) on field `mother` \n 2. `queries[1].sort[0]`, `nested.rankingRules[0]`: descending sort rule(s) on field `father` \n - cannot compare two sort rules in opposite directions \n - note: The ranking rules of query #0 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n - note: The ranking rules of query #1 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
// mix and match query: should be ranked by ranking score
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " nested " , " q " : " pésti " , " sort " : [ " mother:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " nested " , " q " : " jean " , " sort " : [ " father:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #0 and #1 are incompatible: \n 1. `queries[0].sort[0]`, `nested.rankingRules[0]`: ascending sort rule(s) on field `mother` \n 2. `queries[1].sort[0]`, `nested.rankingRules[0]`: descending sort rule(s) on field `father` \n - cannot compare two sort rules in opposite directions \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_different_indexes_same_criterion_same_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " movies " ) ;
let documents = DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let index = server . index ( " batman " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// return titles ordered accross indexes
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " movies " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Escape Room " ,
" id " : " 522681 " ,
" _vectors " : {
" manual " : [
10.0 ,
- 23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
231.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Shazam! " ,
" id " : " 287947 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 10
}
" ###);
// mix and match query: will be sorted across indexes
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " batman " , " q " : " badman returns " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " movies " , " q " : " captain " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " the bat " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _rankingScore " : 0.9848484848484848
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9528218694885362
} ,
" _rankingScore " : 0.9528218694885362
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9528218694885362
} ,
" _rankingScore " : 0.9528218694885362
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8317901234567902
} ,
" _rankingScore " : 0.8317901234567902
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.23106060606060605
} ,
" _rankingScore " : 0.23106060606060605
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.5
} ,
" _rankingScore " : 0.5
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 6
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_different_ranking_rules ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " movies " ) ;
let documents = DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let index = server . index ( " batman " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" sort " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// return titles ordered accross indexes
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " movies " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Escape Room " ,
" id " : " 522681 " ,
" _vectors " : {
" manual " : [
10.0 ,
- 23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
231.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Shazam! " ,
" id " : " 287947 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 10
}
" ###);
// mix and match query: order difficult to understand
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " batman " , " q " : " badman returns " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " movies " , " q " : " captain " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " the bat " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #2 and #1 are incompatible: \n 1. `queries[2]`, `batman.rankingRules[0..=3]`: relevancy rule(s) words, typo, proximity, attribute \n 2. `queries[1].sort[0]`, `movies.rankingRules[0]`: descending sort rule(s) on field `title` \n - cannot compare a relevancy rule with a sort rule \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_different_indexes_same_criterion_opposite_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " movies " ) ;
let documents = DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let index = server . index ( " batman " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// all results from query 0
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " movies " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[0]`: The results of queries #1 and #0 are incompatible: \n 1. `queries[1].sort[0]`, `batman.rankingRules[0]`: descending sort rule(s) on field `title` \n 2. `queries[0].sort[0]`, `movies.rankingRules[0]`: ascending sort rule(s) on field `title` \n - cannot compare two sort rules in opposite directions \n - note: The ranking rules of query #1 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n - note: The ranking rules of query #0 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
// mix and match query: will be sorted by ranking score
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " batman " , " q " : " badman returns " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " movies " , " q " : " captain " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " the bat " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #2 and #1 are incompatible: \n 1. `queries[2].sort[0]`, `batman.rankingRules[0]`: ascending sort rule(s) on field `title` \n 2. `queries[1].sort[0]`, `movies.rankingRules[0]`: descending sort rule(s) on field `title` \n - cannot compare two sort rules in opposite directions \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_different_indexes_different_criterion_same_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " movies " ) ;
let documents = DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let index = server . index ( " batman " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " id " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// return titles ordered accross indexes
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " movies " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " " , " sort " : [ " id:asc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Escape Room " ,
" id " : " 522681 " ,
" _vectors " : {
" manual " : [
10.0 ,
- 23.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
340.0 ,
90.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100.0 ,
231.0 ,
32.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
} ,
{
" title " : " Shazam! " ,
" id " : " 287947 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
3.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _rankingScore " : 1.0
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 10
}
" ###);
// mix and match query: will be sorted across indexes and criterion
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " batman " , " q " : " badman returns " , " sort " : [ " id:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " movies " , " q " : " captain " , " sort " : [ " title:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " the bat " , " sort " : [ " id:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" title " : " Badman " ,
" id " : " E " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.5
} ,
" _rankingScore " : 0.5
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.23106060606060605
} ,
" _rankingScore " : 0.23106060606060605
} ,
{
" title " : " Captain Marvel " ,
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1.0 ,
2.0 ,
54.0
]
} ,
" _federation " : {
" indexUid " : " movies " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _rankingScore " : 0.9848484848484848
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8317901234567902
} ,
" _rankingScore " : 0.8317901234567902
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9528218694885362
} ,
" _rankingScore " : 0.9528218694885362
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _federation " : {
" indexUid " : " batman " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9528218694885362
} ,
" _rankingScore " : 0.9528218694885362
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 6
}
" ###);
}
#[ actix_rt::test ]
async fn federation_sort_different_indexes_different_criterion_opposite_direction ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " movies " ) ;
let documents = DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " title " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let index = server . index ( " batman " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings ( json! ( {
" sortableAttributes " : [ " id " ] ,
" rankingRules " : [
" sort " ,
" words " ,
" typo " ,
" proximity " ,
" attribute " ,
" exactness "
]
} ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
// all results from query 0 first
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " movies " , " q " : " " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " " , " sort " : [ " id:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[0]`: The results of queries #1 and #0 are incompatible: \n 1. `queries[1].sort[0]`, `batman.rankingRules[0]`: descending sort rule(s) on field `id` \n 2. `queries[0].sort[0]`, `movies.rankingRules[0]`: ascending sort rule(s) on field `title` \n - cannot compare two sort rules in opposite directions \n - note: The ranking rules of query #1 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n - note: The ranking rules of query #0 were modified during canonicalization: \n 1. Removed relevancy rule `words` at position #1 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 2. Removed relevancy rule `typo` at position #2 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 3. Removed relevancy rule `proximity` at position #3 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 4. Removed relevancy rule `attribute` at position #4 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n 5. Removed relevancy rule `exactness` at position #5 in ranking rules because the query is a placeholder search (`q`: \" \" ) \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
// mix and match query: more or less by ranking score
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " batman " , " q " : " badman returns " , " sort " : [ " id:desc " ] , " showRankingScore " : true } ,
{ " indexUid " : " movies " , " q " : " captain " , " sort " : [ " title:asc " ] , " showRankingScore " : true } ,
{ " indexUid " : " batman " , " q " : " the bat " , " sort " : [ " id:desc " ] , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: The results of queries #2 and #1 are incompatible: \n 1. `queries[2].sort[0]`, `batman.rankingRules[0]`: descending sort rule(s) on field `id` \n 2. `queries[1].sort[0]`, `movies.rankingRules[0]`: ascending sort rule(s) on field `title` \n - cannot compare two sort rules in opposite directions \n " ,
" code " : " invalid_multi_search_query_ranking_rules " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_ranking_rules "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_limit_offset ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let index = server . index ( " score " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 2 ) . await ;
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
} ,
{
" id " : 852 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman Returns " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 10 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Captain Marvel " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Escape Room " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 3 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 951 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 4 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 654 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.7803030303030303
}
} ,
{
" title " : " Badman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 8 ,
" weightedRankingScore " : 0.5
}
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 6 ,
" weightedRankingScore " : 0.4166666666666667
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 12
}
" ###);
}
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { " limit " : 1 } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 1 ,
" offset " : 0 ,
" estimatedTotalHits " : 12
}
" ###);
}
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { " offset " : 2 } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Batman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman Returns " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 10 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Captain Marvel " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Escape Room " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 3 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 951 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 4 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 654 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.7803030303030303
}
} ,
{
" title " : " Badman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 8 ,
" weightedRankingScore " : 0.5
}
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 6 ,
" weightedRankingScore " : 0.4166666666666667
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 2 ,
" estimatedTotalHits " : 12
}
" ###);
}
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { " offset " : 12 } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [ ] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 12 ,
" estimatedTotalHits " : 12
}
" ###);
}
}
#[ actix_rt::test ]
async fn federation_formatting ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
let index = server . index ( " score " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 2 ) . await ;
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] , " attributesToHighlight " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
} ,
" _formatted " : {
" title " : " <em>Gläss</em> "
}
} ,
{
" id " : 852 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 1.0
} ,
" _formatted " : {
" title " : " <em>Batman</em> "
}
} ,
{
" title " : " Batman Returns " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 10 ,
" weightedRankingScore " : 1.0
} ,
" _formatted " : {
" title " : " <em>Batman</em> <em>Returns</em> "
}
} ,
{
" title " : " Captain Marvel " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _formatted " : {
" title " : " <em>Captain</em> Marvel "
}
} ,
{
" title " : " Escape Room " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 3 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _formatted " : {
" title " : " <em>Escape</em> Room "
}
} ,
{
" id " : 951 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 4 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _formatted " : {
" title " : " <em>Batman</em> the dark knight returns: Part 1 "
}
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
} ,
" _formatted " : {
" title " : " <em>Batman</em> the dark knight returns: Part 2 "
}
} ,
{
" id " : 654 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.7803030303030303
}
} ,
{
" title " : " Badman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 8 ,
" weightedRankingScore " : 0.5
} ,
" _formatted " : {
" title " : " <em>Badman</em> "
}
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 6 ,
" weightedRankingScore " : 0.4166666666666667
} ,
" _formatted " : {
" title " : " How to Train Your Dragon: <em>The</em> Hidden World "
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 12
}
" ###);
}
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { " limit " : 1 } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Gläss " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.0
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 1 ,
" offset " : 0 ,
" estimatedTotalHits " : 12
}
" ###);
}
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { " offset " : 2 } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" title " : " Batman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Batman Returns " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 10 ,
" weightedRankingScore " : 1.0
}
} ,
{
" title " : " Captain Marvel " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Escape Room " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 3 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 951 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 4 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 1 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 9 ,
" weightedRankingScore " : 0.9848484848484848
}
} ,
{
" id " : 654 ,
" _federation " : {
" indexUid " : " nested " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.7803030303030303
}
} ,
{
" title " : " Badman " ,
" _federation " : {
" indexUid " : " score " ,
" queriesPosition " : 8 ,
" weightedRankingScore " : 0.5
}
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" _federation " : {
" indexUid " : " test " ,
" queriesPosition " : 6 ,
" weightedRankingScore " : 0.4166666666666667
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 2 ,
" estimatedTotalHits " : 12
}
" ###);
}
{
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { " offset " : 12 } , " queries " : [
{ " indexUid " : " test " , " q " : " glass " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " captain " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " pésti " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " test " , " q " : " Escape " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " nested " , " q " : " jean " , " attributesToRetrieve " : [ " id " ] } ,
{ " indexUid " : " score " , " q " : " jean " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " test " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " the bat " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " badman returns " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman " , " attributesToRetrieve " : [ " title " ] } ,
{ " indexUid " : " score " , " q " : " batman returns " , " attributesToRetrieve " : [ " title " ] } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [ ] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 12 ,
" estimatedTotalHits " : 12
}
" ###);
}
}
#[ actix_rt::test ]
async fn federation_invalid_weight ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " fruits " ) ;
let documents = FRUITS_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings (
json! ( { " searchableAttributes " : [ " name " ] , " filterableAttributes " : [ " BOOST " ] } ) ,
)
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " , " filter " : " BOOST = true " , " showRankingScore " : true , " federationOptions " : { " weight " : 3.0 } } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " showRankingScore " : true , " federationOptions " : { " weight " : - 12 } } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Invalid value at `.queries[1].federationOptions.weight`: the value of `weight` is invalid, expected a positive float (>= 0.0). " ,
" code " : " invalid_multi_search_weight " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_weight "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_null_weight ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " fruits " ) ;
let documents = FRUITS_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( value , _ ) = index
. update_settings (
json! ( { " searchableAttributes " : [ " name " ] , " filterableAttributes " : [ " BOOST " ] } ) ,
)
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " , " filter " : " BOOST = true " , " showRankingScore " : true , " federationOptions " : { " weight " : 3.0 } } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " showRankingScore " : true , " federationOptions " : { " weight " : 0.0 } } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" hits " : [
{
" name " : " Exclusive sale: Red delicious apple " ,
" id " : " red-delicious-boosted " ,
" BOOST " : true ,
" _federation " : {
" indexUid " : " fruits " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 2.7281746031746033
} ,
" _rankingScore " : 0.9093915343915344
} ,
{
" name " : " Exclusive sale: green apple " ,
" id " : " green-apple-boosted " ,
" BOOST " : true ,
" _federation " : {
" indexUid " : " fruits " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 1.318181818181818
} ,
" _rankingScore " : 0.4393939393939394
} ,
{
" name " : " Red apple gala " ,
" id " : " red-apple-gala " ,
" _federation " : {
" indexUid " : " fruits " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.0
} ,
" _rankingScore " : 0.953042328042328
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 3
}
" ###);
}
#[ actix_rt::test ]
async fn federation_federated_contains_pagination ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " fruits " ) ;
let documents = FRUITS_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
// fail when a federated query contains "limit"
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " limit " : 5 } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: Using pagination options is not allowed in federated queries. \n Hint: remove `limit` from query #1 or remove `federation: {}` from the request " ,
" code " : " invalid_multi_search_query_pagination " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_pagination "
}
" ###);
// fail when a federated query contains "offset"
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " offset " : 5 } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: Using pagination options is not allowed in federated queries. \n Hint: remove `offset` from query #1 or remove `federation: {}` from the request " ,
" code " : " invalid_multi_search_query_pagination " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_pagination "
}
" ###);
// fail when a federated query contains "page"
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " page " : 2 } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: Using pagination options is not allowed in federated queries. \n Hint: remove `page` from query #1 or remove `federation: {}` from the request " ,
" code " : " invalid_multi_search_query_pagination " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_pagination "
}
" ###);
// fail when a federated query contains "hitsPerPage"
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " hitsPerPage " : 5 } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: Using pagination options is not allowed in federated queries. \n Hint: remove `hitsPerPage` from query #1 or remove `federation: {}` from the request " ,
" code " : " invalid_multi_search_query_pagination " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_query_pagination "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_non_federated_contains_federation_option ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " fruits " ) ;
let documents = FRUITS_DOCUMENTS . clone ( ) ;
let ( value , _ ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( value . uid ( ) ) . await ;
// fail when a non-federated query contains "federationOptions"
let ( response , code ) = server
. multi_search ( json! ( { " queries " : [
{ " indexUid " : " fruits " , " q " : " apple red " } ,
{ " indexUid " : " fruits " , " q " : " apple red " , " federationOptions " : { } } ,
] } ) )
. await ;
snapshot! ( code , @ " 400 Bad Request " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " } , @ r ###"
{
" message " : " Inside `.queries[1]`: Using `federationOptions` is not allowed in a non-federated search. \n Hint: remove `federationOptions` from query #1 or add `federation: {}` to the request. " ,
" code " : " invalid_multi_search_federation_options " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#invalid_multi_search_federation_options "
}
" ###);
}
#[ actix_rt::test ]
async fn federation_vector_single_index ( ) {
let server = Server ::new ( ) . await ;
let ( _ , code ) = server
. set_features ( json! ( {
" vectorStore " : true
} ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
let index = server . index ( " vectors " ) ;
let ( value , _ ) = index
. update_settings ( json! ( { " embedders " : {
" animal " : {
" source " : " userProvided " ,
" dimensions " : 3
} ,
" sentiment " : {
" source " : " userProvided " ,
" dimensions " : 2
}
} } ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let documents = VECTOR_DOCUMENTS . clone ( ) ;
let ( value , code ) = index . add_documents ( documents , None ) . await ;
snapshot! ( code , @ " 202 Accepted " ) ;
index . wait_task ( value . uid ( ) ) . await ;
// same embedder
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " vectors " , " vector " : [ 1.0 , 0.0 , 0.5 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " animal " } } ,
{ " indexUid " : " vectors " , " vector " : [ 0.5 , 0.5 , 0.5 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " animal " } } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9870882034301758
}
} ,
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9728479385375975
}
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9701486229896544
}
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9191691875457764
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 4 ,
" semanticHitCount " : 4
}
" ###);
// distinct embedder
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " vectors " , " vector " : [ 1.0 , 0.0 , 0.5 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " animal " } } ,
// joyful and energetic first
{ " indexUid " : " vectors " , " vector " : [ 0.8 , 0.6 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " sentiment " } } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.979868710041046
}
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9701486229896544
}
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8601469993591309
}
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8432406187057495
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 4 ,
" semanticHitCount " : 4
}
" ###);
// hybrid search, distinct embedder
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " vectors " , " vector " : [ 1.0 , 0.0 , 0.5 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " animal " } , " showRankingScore " : true } ,
// joyful and energetic first
{ " indexUid " : " vectors " , " vector " : [ 0.8 , 0.6 ] , " q " : " beagle " , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " sentiment " } , " showRankingScore " : true } ,
{ " indexUid " : " vectors " , " q " : " dog " , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.979868710041046
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9701486229896544
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9242424242424242
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _federation " : {
" indexUid " : " vectors " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8601469993591309
} ,
" _rankingScore " : " [score] "
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 4 ,
" semanticHitCount " : 3
}
" ###);
}
#[ actix_rt::test ]
async fn federation_vector_two_indexes ( ) {
let server = Server ::new ( ) . await ;
let ( _ , code ) = server
. set_features ( json! ( {
" vectorStore " : true
} ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
let index = server . index ( " vectors-animal " ) ;
let ( value , _ ) = index
. update_settings ( json! ( { " embedders " : {
" animal " : {
" source " : " userProvided " ,
" dimensions " : 3
} ,
} } ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let documents = VECTOR_DOCUMENTS . clone ( ) ;
let ( value , code ) = index . add_documents ( documents , None ) . await ;
snapshot! ( code , @ " 202 Accepted " ) ;
index . wait_task ( value . uid ( ) ) . await ;
let index = server . index ( " vectors-sentiment " ) ;
let ( value , _ ) = index
. update_settings ( json! ( { " embedders " : {
" sentiment " : {
" source " : " userProvided " ,
" dimensions " : 2
}
} } ) )
. await ;
index . wait_task ( value . uid ( ) ) . await ;
let documents = VECTOR_DOCUMENTS . clone ( ) ;
let ( value , code ) = index . add_documents ( documents , None ) . await ;
snapshot! ( code , @ " 202 Accepted " ) ;
index . wait_task ( value . uid ( ) ) . await ;
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " vectors-animal " , " vector " : [ 1.0 , 0.0 , 0.5 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " animal " } } ,
// joyful and energetic first
{ " indexUid " : " vectors-sentiment " , " vector " : [ 0.8 , 0.6 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " sentiment " } } ,
{ " indexUid " : " vectors-sentiment " , " q " : " dog " } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _vectors " : {
" animal " : [
0.8 ,
0.09 ,
0.8
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.979868710041046
}
} ,
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _vectors " : {
" sentiment " : [
0.8 ,
0.3
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9728479385375975
}
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _vectors " : {
" sentiment " : [
- 1.0 ,
0.1
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9701486229896544
}
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _vectors " : {
" animal " : [
0.9 ,
0.8 ,
0.05
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9242424242424242
}
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _vectors " : {
" animal " : [
0.85 ,
0.02 ,
0.1
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 2 ,
" weightedRankingScore " : 0.9242424242424242
}
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _vectors " : {
" sentiment " : [
- 0.2 ,
0.65
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8601469993591309
}
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _vectors " : {
" sentiment " : [
- 0.1 ,
0.55
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8432406187057495
}
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _vectors " : {
" animal " : [
0.8 ,
0.9 ,
0.5
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.6690993905067444
}
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 8 ,
" semanticHitCount " : 6
}
" ###);
// hybrid search, distinct embedder
let ( response , code ) = server
. multi_search ( json! ( { " federation " : { } , " queries " : [
{ " indexUid " : " vectors-animal " , " vector " : [ 1.0 , 0.0 , 0.5 ] , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " animal " } , " showRankingScore " : true } ,
{ " indexUid " : " vectors-sentiment " , " vector " : [ - 1 , 0.6 ] , " q " : " beagle " , " hybrid " : { " semanticRatio " : 1.0 , " embedder " : " sentiment " } , " showRankingScore " : true } ,
] } ) )
. await ;
snapshot! ( code , @ " 200 OK " ) ;
insta ::assert_json_snapshot! ( response , { " .processingTimeMs " = > " [time] " , " .**._rankingScore " = > " [score] " } , @ r ###"
{
" hits " : [
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _vectors " : {
" sentiment " : [
0.8 ,
0.3
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9728479385375975
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _vectors " : {
" sentiment " : [
- 1.0 ,
0.1
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.9701486229896544
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _vectors " : {
" animal " : [
0.85 ,
0.02 ,
0.1
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.9522157907485962
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _vectors " : {
" animal " : [
0.8 ,
0.9 ,
0.5
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.8719604015350342
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _vectors " : {
" sentiment " : [
- 0.2 ,
0.65
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8601469993591309
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _vectors " : {
" sentiment " : [
- 0.1 ,
0.55
]
} ,
" _federation " : {
" indexUid " : " vectors-animal " ,
" queriesPosition " : 0 ,
" weightedRankingScore " : 0.8432406187057495
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _vectors " : {
" animal " : [
0.9 ,
0.8 ,
0.05
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.8297949433326721
} ,
" _rankingScore " : " [score] "
} ,
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _vectors " : {
" animal " : [
0.8 ,
0.09 ,
0.8
]
} ,
" _federation " : {
" indexUid " : " vectors-sentiment " ,
" queriesPosition " : 1 ,
" weightedRankingScore " : 0.18887794017791748
} ,
" _rankingScore " : " [score] "
}
] ,
" processingTimeMs " : " [time] " ,
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 8 ,
" semanticHitCount " : 8
}
" ###);
}