2022-06-28 00:58:11 +02:00
// This modules contains all the test concerning search. Each particular feature of the search
2021-02-18 19:50:52 +01:00
// should be tested in its own module to isolate tests and keep the tests readable.
2021-07-06 11:54:37 +02:00
2023-10-09 12:49:36 +02:00
mod distinct ;
2021-07-06 11:54:37 +02:00
mod errors ;
2023-06-28 11:31:52 +02:00
mod facet_search ;
2022-04-19 16:49:38 +02:00
mod formatted ;
2023-07-17 18:28:03 +02:00
mod geo ;
2023-12-13 11:47:12 +01:00
mod hybrid ;
2023-02-06 13:46:12 +01:00
mod multi ;
2022-10-20 17:03:07 +02:00
mod pagination ;
2023-06-07 18:49:17 +02:00
mod restrict_searchable ;
2021-07-06 11:54:37 +02:00
use once_cell ::sync ::Lazy ;
2023-09-11 16:50:53 +02:00
use crate ::common ::{ Server , Value } ;
use crate ::json ;
2022-10-20 18:00:07 +02:00
2023-11-23 12:20:44 +01:00
static DOCUMENTS : Lazy < Value > = Lazy ::new ( | | {
2021-07-06 11:54:37 +02:00
json! ( [
{
" title " : " Shazam! " ,
2022-03-30 13:47:27 +02:00
" id " : " 287947 " ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ 1 , 2 , 3 ] } ,
2021-07-06 11:54:37 +02:00
} ,
{
" title " : " Captain Marvel " ,
2022-03-30 13:47:27 +02:00
" id " : " 299537 " ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ 1 , 2 , 54 ] } ,
2021-07-06 11:54:37 +02:00
} ,
{
" title " : " Escape Room " ,
2022-03-30 13:47:27 +02:00
" id " : " 522681 " ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ 10 , - 23 , 32 ] } ,
2021-07-06 11:54:37 +02:00
} ,
2022-03-30 13:47:27 +02:00
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" id " : " 166428 " ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ - 100 , 231 , 32 ] } ,
2021-07-06 11:54:37 +02:00
} ,
{
2023-03-29 10:58:05 +02:00
" title " : " Gläss " ,
2022-03-30 13:47:27 +02:00
" id " : " 450465 " ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ - 100 , 340 , 90 ] } ,
2021-07-06 11:54:37 +02:00
}
] )
} ) ;
2023-11-23 12:20:44 +01:00
static NESTED_DOCUMENTS : Lazy < Value > = Lazy ::new ( | | {
2022-03-30 13:47:27 +02:00
json! ( [
{
" id " : 852 ,
" father " : " jean " ,
" mother " : " michelle " ,
" doggos " : [
{
" name " : " bobby " ,
" age " : 2 ,
} ,
{
" name " : " buddy " ,
" age " : 4 ,
} ,
] ,
2023-03-29 10:58:05 +02:00
" cattos " : " pésti " ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ 1 , 2 , 3 ] } ,
2022-03-30 13:47:27 +02: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 " : [ 1 , 2 , 54 ] } ,
2022-03-30 13:47:27 +02:00
} ,
{
" id " : 750 ,
" father " : " romain " ,
" mother " : " michelle " ,
" cattos " : [ " enigma " ] ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ 10 , 23 , 32 ] } ,
2022-03-30 13:47:27 +02:00
} ,
{
" id " : 951 ,
" father " : " jean-baptiste " ,
" mother " : " sophie " ,
" doggos " : [
{
" name " : " turbo " ,
" age " : 5 ,
} ,
{
" name " : " fast " ,
" age " : 6 ,
} ,
] ,
" cattos " : [ " moumoute " , " gomez " ] ,
2023-12-13 21:49:13 +01:00
" _vectors " : { " manual " : [ 10 , 23 , 32 ] } ,
2022-03-30 13:47:27 +02:00
} ,
] )
} ) ;
2021-07-06 11:54:37 +02:00
#[ actix_rt::test ]
async fn simple_placeholder_search ( ) {
let server = Server ::new ( ) . await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " basic " ) ;
2021-07-06 11:54:37 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 0 ) . await ;
2021-07-06 11:54:37 +02:00
index
. search ( json! ( { } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 5 ) ;
} )
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
index
. search ( json! ( { } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 4 ) ;
} )
. await ;
2021-07-06 11:54:37 +02:00
}
#[ actix_rt::test ]
async fn simple_search ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 0 ) . await ;
2021-07-06 11:54:37 +02:00
index
. search ( json! ( { " q " : " glass " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} )
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
index
2023-03-29 10:58:05 +02:00
. search ( json! ( { " q " : " pésti " } ) , | response , code | {
2022-03-30 13:47:27 +02:00
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 2 ) ;
} )
. await ;
2021-07-06 11:54:37 +02:00
}
2023-02-21 18:02:10 +01:00
#[ actix_rt::test ]
async fn phrase_search_with_stop_word ( ) {
// related to https://github.com/meilisearch/meilisearch/issues/3521
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let ( _ , code ) = index . update_settings ( json! ( { " stopWords " : [ " the " , " of " ] } ) ) . await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
index
. search ( json! ( { " q " : " how \" to \" train \" the " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} )
. await ;
}
2023-02-20 15:43:29 +01:00
#[ cfg(feature = " default " ) ]
#[ actix_rt::test ]
async fn test_kanji_language_detection ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = json! ( [
{ " id " : 0 , " title " : " The quick ( \" brown \" ) fox can't jump 32.3 feet, right? Brr, it's 29.3°F! " } ,
{ " id " : 1 , " title " : " 東京のお寿司。 " } ,
{ " id " : 2 , " title " : " הַשּׁוּעָל הַמָּהִיר (״הַחוּם״) לֹא יָכוֹל לִקְפֹּץ 9.94 מֶטְרִים, נָכוֹן? ברר, 1.5°C- בַּחוּץ! " }
] ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
index
. search ( json! ( { " q " : " 東京 " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} )
. await ;
}
2023-03-09 14:56:13 +01:00
#[ cfg(feature = " default " ) ]
#[ actix_rt::test ]
async fn test_thai_language ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
// We don't need documents, the issue is on the query side only.
let documents = json! ( [
{ " id " : 0 , " title " : " สบู่สมุนไพรดอกดาวเรือง 100 กรัม จำนวน 6 ก้อน " } ,
{ " id " : 1 , " title " : " สบู่สมุนไพรชาเขียว 100 กรัม จำนวน 6 ก้อน " } ,
{ " id " : 2 , " title " : " สบู่สมุนไพรฝางแดงผสมว่านหางจรเข้ 100 กรัม จำนวน 6 ก้อน " }
] ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
index . update_settings ( json! ( { " rankingRules " : [ " exactness " ] } ) ) . await ;
index . wait_task ( 1 ) . await ;
index
. search ( json! ( { " q " : " สบู " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
} )
. await ;
}
2021-07-06 11:54:37 +02:00
#[ actix_rt::test ]
async fn search_multiple_params ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 0 ) . await ;
2021-07-06 11:54:37 +02:00
index
. search (
json! ( {
" q " : " glass " ,
" attributesToCrop " : [ " title:2 " ] ,
" attributesToHighlight " : [ " title " ] ,
" limit " : 1 ,
" offset " : 0 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} ,
)
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
index
. search (
json! ( {
2023-03-29 10:58:05 +02:00
" q " : " pésti " ,
2022-03-30 13:47:27 +02:00
" attributesToCrop " : [ " catto:2 " ] ,
" attributesToHighlight " : [ " catto " ] ,
" limit " : 2 ,
" offset " : 0 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 2 ) ;
} ,
)
. await ;
2021-07-06 11:54:37 +02:00
}
#[ actix_rt::test ]
async fn search_with_filter_string_notation ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2023-01-12 15:39:28 +01:00
let ( _ , code ) = index . update_settings ( json! ( { " filterableAttributes " : [ " title " ] } ) ) . await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
2021-07-06 11:54:37 +02:00
let documents = DOCUMENTS . clone ( ) ;
2023-01-12 15:39:28 +01:00
let ( _ , code ) = index . add_documents ( documents , None ) . await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
let res = index . wait_task ( 1 ) . await ;
meili_snap ::snapshot! ( res [ " status " ] , @ r ### ""succeeded""### ) ;
2021-07-06 11:54:37 +02:00
index
. search (
json! ( {
2023-03-29 10:58:05 +02:00
" filter " : " title = Gläss "
2021-07-06 11:54:37 +02:00
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} ,
)
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
2023-01-12 15:39:28 +01:00
let ( _ , code ) =
index . update_settings ( json! ( { " filterableAttributes " : [ " cattos " , " doggos.age " ] } ) ) . await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
2022-03-30 13:47:27 +02:00
let documents = NESTED_DOCUMENTS . clone ( ) ;
2023-01-12 15:39:28 +01:00
let ( _ , code ) = index . add_documents ( documents , None ) . await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
let res = index . wait_task ( 3 ) . await ;
meili_snap ::snapshot! ( res [ " status " ] , @ r ### ""succeeded""### ) ;
2022-03-30 13:47:27 +02:00
index
. search (
json! ( {
2023-03-29 10:58:05 +02:00
" filter " : " cattos = pésti "
2022-03-30 13:47:27 +02:00
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
assert_eq! ( response [ " hits " ] [ 0 ] [ " id " ] , json! ( 852 ) ) ;
} ,
)
. await ;
index
. search (
json! ( {
" filter " : " doggos.age > 5 "
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 2 ) ;
assert_eq! ( response [ " hits " ] [ 0 ] [ " id " ] , json! ( 654 ) ) ;
assert_eq! ( response [ " hits " ] [ 1 ] [ " id " ] , json! ( 951 ) ) ;
} ,
)
. await ;
2021-07-06 11:54:37 +02:00
}
#[ actix_rt::test ]
async fn search_with_filter_array_notation ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " filterableAttributes " : [ " title " ] } ) ) . await ;
2021-07-06 11:54:37 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 1 ) . await ;
2021-07-06 11:54:37 +02:00
let ( response , code ) = index
. search_post ( json! ( {
2023-03-29 10:58:05 +02:00
" filter " : [ " title = Gläss " ]
2021-07-06 11:54:37 +02:00
} ) )
. await ;
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
let ( response , code ) = index
. search_post ( json! ( {
2023-03-29 10:58:05 +02:00
" filter " : [ [ " title = Gläss " , " title = \" Shazam! \" " , " title = \" Escape Room \" " ] ]
2021-07-06 11:54:37 +02:00
} ) )
. await ;
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 3 ) ;
}
2021-08-24 15:45:16 +02:00
#[ actix_rt::test ]
async fn search_with_sort_on_numbers ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " sortableAttributes " : [ " id " ] } ) ) . await ;
2021-08-24 15:45:16 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 1 ) . await ;
2021-08-24 15:45:16 +02:00
index
. search (
json! ( {
" sort " : [ " id:asc " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 5 ) ;
} ,
)
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " sortableAttributes " : [ " doggos.age " ] } ) ) . await ;
2022-03-30 13:47:27 +02:00
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 3 ) . await ;
index
. search (
json! ( {
" sort " : [ " doggos.age:asc " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 4 ) ;
} ,
)
. await ;
2021-08-24 15:45:16 +02:00
}
#[ actix_rt::test ]
async fn search_with_sort_on_strings ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " sortableAttributes " : [ " title " ] } ) ) . await ;
2021-08-24 15:45:16 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 1 ) . await ;
2021-08-24 15:45:16 +02:00
index
. search (
json! ( {
" sort " : [ " title:desc " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 5 ) ;
} ,
)
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " sortableAttributes " : [ " doggos.name " ] } ) ) . await ;
2022-03-30 13:47:27 +02:00
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 3 ) . await ;
index
. search (
json! ( {
" sort " : [ " doggos.name:asc " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 4 ) ;
} ,
)
. await ;
2021-08-24 15:45:16 +02:00
}
#[ actix_rt::test ]
async fn search_with_multiple_sort ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " sortableAttributes " : [ " id " , " title " ] } ) ) . await ;
2021-08-24 15:45:16 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 1 ) . await ;
2021-08-24 15:45:16 +02:00
let ( response , code ) = index
. search_post ( json! ( {
" sort " : [ " id:asc " , " title:desc " ]
} ) )
. await ;
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 5 ) ;
}
2021-07-06 11:54:37 +02:00
#[ actix_rt::test ]
async fn search_facet_distribution ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " filterableAttributes " : [ " title " ] } ) ) . await ;
2021-07-06 11:54:37 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 1 ) . await ;
2021-07-06 11:54:37 +02:00
index
. search (
json! ( {
2022-05-18 13:17:56 +02:00
" facets " : [ " title " ]
2021-07-06 11:54:37 +02:00
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
2022-05-18 13:17:56 +02:00
let dist = response [ " facetDistribution " ] . as_object ( ) . unwrap ( ) ;
2021-07-06 11:54:37 +02:00
assert_eq! ( dist . len ( ) , 1 ) ;
assert! ( dist . get ( " title " ) . is_some ( ) ) ;
} ,
)
. await ;
2022-03-30 13:47:27 +02:00
let index = server . index ( " nested " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " filterableAttributes " : [ " father " , " doggos.name " ] } ) ) . await ;
2022-03-30 13:47:27 +02:00
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 3 ) . await ;
// TODO: TAMO: fix the test
index
. search (
json! ( {
2022-05-18 13:17:56 +02:00
// "facets": ["father", "doggos.name"]
" facets " : [ " father " ]
2022-03-30 13:47:27 +02:00
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
2022-05-18 13:17:56 +02:00
let dist = response [ " facetDistribution " ] . as_object ( ) . unwrap ( ) ;
2022-03-30 13:47:27 +02:00
assert_eq! ( dist . len ( ) , 1 ) ;
assert_eq! (
dist [ " father " ] ,
json! ( { " jean " : 1 , " pierre " : 1 , " romain " : 1 , " jean-baptiste " : 1 } )
) ;
/*
assert_eq! (
dist [ " doggos.name " ] ,
json! ( { " bobby " : 1 , " buddy " : 1 , " gros bill " : 1 , " turbo " : 1 , " fast " : 1 } )
) ;
* /
} ,
)
. await ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " filterableAttributes " : [ " doggos " ] } ) ) . await ;
2022-03-30 13:47:27 +02:00
index . wait_task ( 4 ) . await ;
index
. search (
json! ( {
2022-05-18 13:17:56 +02:00
" facets " : [ " doggos.name " ]
2022-03-30 13:47:27 +02:00
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
2022-05-18 13:17:56 +02:00
let dist = response [ " facetDistribution " ] . as_object ( ) . unwrap ( ) ;
2022-03-30 13:47:27 +02:00
assert_eq! ( dist . len ( ) , 1 ) ;
assert_eq! (
dist [ " doggos.name " ] ,
json! ( { " bobby " : 1 , " buddy " : 1 , " gros bill " : 1 , " turbo " : 1 , " fast " : 1 } )
) ;
} ,
)
. await ;
index
. search (
json! ( {
2022-05-18 13:17:56 +02:00
" facets " : [ " doggos " ]
2022-03-30 13:47:27 +02:00
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
2022-05-18 13:17:56 +02:00
let dist = response [ " facetDistribution " ] . as_object ( ) . unwrap ( ) ;
2022-05-04 12:10:52 +02:00
assert_eq! ( dist . len ( ) , 3 ) ;
2022-03-30 13:47:27 +02:00
assert_eq! (
dist [ " doggos.name " ] ,
json! ( { " bobby " : 1 , " buddy " : 1 , " gros bill " : 1 , " turbo " : 1 , " fast " : 1 } )
) ;
2022-10-20 18:00:07 +02:00
assert_eq! ( dist [ " doggos.age " ] , json! ( { " 2 " : 1 , " 4 " : 1 , " 5 " : 1 , " 6 " : 1 , " 8 " : 1 } ) ) ;
2022-03-30 13:47:27 +02:00
} ,
)
. await ;
2021-07-06 11:54:37 +02:00
}
#[ actix_rt::test ]
async fn displayed_attributes ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " displayedAttributes " : [ " title " ] } ) ) . await ;
2021-07-06 11:54:37 +02:00
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
2021-12-02 16:03:26 +01:00
index . wait_task ( 1 ) . await ;
2021-07-06 11:54:37 +02:00
2022-10-20 18:00:07 +02:00
let ( response , code ) =
index . search_post ( json! ( { " attributesToRetrieve " : [ " title " , " id " ] } ) ) . await ;
2021-07-06 11:54:37 +02:00
assert_eq! ( code , 200 , " {} " , response ) ;
2022-03-30 13:47:27 +02:00
assert! ( response [ " hits " ] [ 0 ] . get ( " title " ) . is_some ( ) ) ;
2021-07-06 11:54:37 +02:00
}
2022-03-31 01:32:37 +02:00
#[ actix_rt::test ]
async fn placeholder_search_is_hard_limited ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
let documents : Vec < _ > = ( 0 .. 1200 ) . map ( | i | json! ( { " id " : i , " text " : " I am unique! " } ) ) . collect ( ) ;
2022-03-31 01:32:37 +02:00
index . add_documents ( documents . into ( ) , None ) . await ;
index . wait_task ( 0 ) . await ;
index
. search (
json! ( {
" limit " : 1500 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1000 ) ;
} ,
)
. await ;
index
. search (
json! ( {
" offset " : 800 ,
" limit " : 400 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 200 ) ;
} ,
)
. await ;
2022-06-09 10:48:32 +02:00
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " pagination " : { " maxTotalHits " : 10_000 } } ) ) . await ;
2022-06-09 10:48:32 +02:00
index . wait_task ( 1 ) . await ;
index
. search (
json! ( {
" limit " : 1500 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1200 ) ;
} ,
)
. await ;
index
. search (
json! ( {
" offset " : 1000 ,
" limit " : 400 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 200 ) ;
} ,
)
. await ;
2022-03-31 01:32:37 +02:00
}
#[ actix_rt::test ]
async fn search_is_hard_limited ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
let documents : Vec < _ > = ( 0 .. 1200 ) . map ( | i | json! ( { " id " : i , " text " : " I am unique! " } ) ) . collect ( ) ;
2022-03-31 01:32:37 +02:00
index . add_documents ( documents . into ( ) , None ) . await ;
index . wait_task ( 0 ) . await ;
index
. search (
json! ( {
" q " : " unique " ,
" limit " : 1500 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1000 ) ;
} ,
)
. await ;
index
. search (
json! ( {
" q " : " unique " ,
" offset " : 800 ,
" limit " : 400 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 200 ) ;
} ,
)
. await ;
2022-06-09 10:48:32 +02:00
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " pagination " : { " maxTotalHits " : 10_000 } } ) ) . await ;
2022-06-09 10:48:32 +02:00
index . wait_task ( 1 ) . await ;
index
. search (
json! ( {
" q " : " unique " ,
" limit " : 1500 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1200 ) ;
} ,
)
. await ;
index
. search (
json! ( {
" q " : " unique " ,
" offset " : 1000 ,
" limit " : 400 ,
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 200 ) ;
} ,
)
. await ;
2022-03-31 01:32:37 +02:00
}
2022-06-09 10:41:46 +02:00
#[ actix_rt::test ]
async fn faceting_max_values_per_facet ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " filterableAttributes " : [ " number " ] } ) ) . await ;
2022-06-09 10:41:46 +02:00
2022-10-20 18:00:07 +02:00
let documents : Vec < _ > = ( 0 .. 10_000 ) . map ( | id | json! ( { " id " : id , " number " : id * 10 } ) ) . collect ( ) ;
2022-06-09 10:41:46 +02:00
index . add_documents ( json! ( documents ) , None ) . await ;
index . wait_task ( 1 ) . await ;
index
. search (
json! ( {
" facets " : [ " number " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let numbers = response [ " facetDistribution " ] [ " number " ] . as_object ( ) . unwrap ( ) ;
assert_eq! ( numbers . len ( ) , 100 ) ;
} ,
)
. await ;
2022-10-20 18:00:07 +02:00
index . update_settings ( json! ( { " faceting " : { " maxValuesPerFacet " : 10_000 } } ) ) . await ;
2022-06-09 10:41:46 +02:00
index . wait_task ( 2 ) . await ;
index
. search (
json! ( {
" facets " : [ " number " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
2022-06-16 15:58:39 +02:00
let numbers = & response [ " facetDistribution " ] [ " number " ] . as_object ( ) . unwrap ( ) ;
2022-06-09 10:41:46 +02:00
assert_eq! ( numbers . len ( ) , 10_000 ) ;
} ,
)
. await ;
}
2023-07-06 09:02:02 +02:00
#[ actix_rt::test ]
async fn experimental_feature_score_details ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( json! ( documents ) , None ) . await ;
index . wait_task ( 0 ) . await ;
index
. search (
json! ( {
" q " : " train dragon " ,
" showRankingScoreDetails " : true ,
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 400 Bad Request " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response ) , @ r ###"
{
" message " : " Computing score details requires enabling the `score details` experimental feature. See https://github.com/meilisearch/product/discussions/674 " ,
" code " : " feature_not_enabled " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#feature_not_enabled "
}
" ###);
} ,
)
. await ;
let ( response , code ) = server . set_features ( json! ( { " scoreDetails " : true } ) ) . await ;
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( response [ " scoreDetails " ] , @ " true " ) ;
index
. search (
json! ( {
" q " : " train dragon " ,
" showRankingScoreDetails " : true ,
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " How to Train Your Dragon: The Hidden World " ,
" id " : " 166428 " ,
2023-12-13 21:49:13 +01:00
" _vectors " : {
" manual " : [
- 100 ,
231 ,
32
]
} ,
2023-07-06 09:02:02 +02:00
" _rankingScoreDetails " : {
" words " : {
" order " : 0 ,
" matchingWords " : 2 ,
" maxMatchingWords " : 2 ,
" score " : 1.0
} ,
" typo " : {
" order " : 1 ,
" typoCount " : 0 ,
" maxTypoCount " : 2 ,
" score " : 1.0
} ,
" proximity " : {
" order " : 2 ,
2023-10-17 17:19:45 +02:00
" score " : 0.75
2023-07-06 09:02:02 +02:00
} ,
" attribute " : {
" order " : 3 ,
2023-07-25 15:49:33 +02:00
" attributeRankingOrderScore " : 1.0 ,
" queryWordDistanceScore " : 0.8095238095238095 ,
2023-12-13 21:49:13 +01:00
" score " : 0.9727891156462584
2023-07-06 09:02:02 +02:00
} ,
" exactness " : {
" order " : 4 ,
" matchType " : " noExactMatch " ,
" matchingWords " : 2 ,
" maxMatchingWords " : 2 ,
" score " : 0.3333333333333333
}
}
}
]
" ###);
} ,
)
. await ;
}
#[ actix_rt::test ]
async fn experimental_feature_vector_store ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( json! ( documents ) , None ) . await ;
index . wait_task ( 0 ) . await ;
let ( response , code ) = index
. search_post ( json! ( {
" vector " : [ 1.0 , 2.0 , 3.0 ] ,
} ) )
. await ;
meili_snap ::snapshot! ( code , @ " 400 Bad Request " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response ) , @ r ###"
{
" message " : " Passing `vector` as a query parameter requires enabling the `vector store` experimental feature. See https://github.com/meilisearch/product/discussions/677 " ,
" code " : " feature_not_enabled " ,
" type " : " invalid_request " ,
" link " : " https://docs.meilisearch.com/errors#feature_not_enabled "
}
" ###);
let ( response , code ) = server . set_features ( json! ( { " vectorStore " : true } ) ) . await ;
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( response [ " vectorStore " ] , @ " true " ) ;
2023-12-13 21:49:13 +01:00
let ( response , code ) = index
. update_settings ( json! ( { " embedders " : {
" manual " : {
" source " : {
" userProvided " : { " dimensions " : 3 }
}
}
} } ) )
. await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
let response = index . wait_task ( response . uid ( ) ) . await ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " status " ] ) , @ " \" succeeded \" " ) ;
2023-07-06 09:02:02 +02:00
let ( response , code ) = index
. search_post ( json! ( {
" vector " : [ 1.0 , 2.0 , 3.0 ] ,
} ) )
. await ;
2023-12-13 21:49:13 +01:00
2023-07-06 09:02:02 +02:00
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
2023-12-12 23:39:01 +01:00
// vector search returns all documents that don't have vectors in the last bucket, like all sorts
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " Shazam! " ,
2023-12-13 21:49:13 +01:00
" id " : " 287947 " ,
" _vectors " : {
" manual " : [
1 ,
2 ,
3
]
} ,
" _semanticScore " : 1.0
2023-12-12 23:39:01 +01:00
} ,
{
" title " : " Captain Marvel " ,
2023-12-13 21:49:13 +01:00
" id " : " 299537 " ,
" _vectors " : {
" manual " : [
1 ,
2 ,
54
]
} ,
" _semanticScore " : 0.9129112
2023-12-12 23:39:01 +01:00
} ,
{
2023-12-13 21:49:13 +01:00
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
" manual " : [
- 100 ,
340 ,
90
]
} ,
" _semanticScore " : 0.8106413
2023-12-12 23:39:01 +01:00
} ,
{
" title " : " How to Train Your Dragon: The Hidden World " ,
2023-12-13 21:49:13 +01:00
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100 ,
231 ,
32
]
} ,
" _semanticScore " : 0.74120104
2023-12-12 23:39:01 +01:00
} ,
{
2023-12-13 21:49:13 +01:00
" title " : " Escape Room " ,
" id " : " 522681 " ,
" _vectors " : {
" manual " : [
10 ,
- 23 ,
32
]
}
2023-12-12 23:39:01 +01:00
}
]
" ###);
2023-07-06 09:02:02 +02:00
}
2023-07-10 14:01:14 +02:00
#[ cfg(feature = " default " ) ]
#[ actix_rt::test ]
async fn camelcased_words ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
// related to https://github.com/meilisearch/meilisearch/issues/3818
let documents = json! ( [
{ " id " : 0 , " title " : " DeLonghi " } ,
{ " id " : 1 , " title " : " delonghi " } ,
{ " id " : 2 , " title " : " TestAB " } ,
2023-07-12 16:47:30 +02:00
{ " id " : 3 , " title " : " TestAb " } ,
{ " id " : 4 , " title " : " testab " } ,
2023-07-10 14:01:14 +02:00
] ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 0 ) . await ;
index
. search ( json! ( { " q " : " deLonghi " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 0 ,
" title " : " DeLonghi "
} ,
{
" id " : 1 ,
" title " : " delonghi "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " dellonghi " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 0 ,
" title " : " DeLonghi "
} ,
{
" id " : 1 ,
" title " : " delonghi "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " testa " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " testab " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " TestaB " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " Testab " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " TestAb " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
2023-07-12 16:47:30 +02:00
// with Typos
2023-07-10 14:01:14 +02:00
index
2023-07-12 16:47:30 +02:00
. search ( json! ( { " q " : " dellonghi " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 0 ,
" title " : " DeLonghi "
} ,
{
" id " : 1 ,
" title " : " delonghi "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " TetsAB " } ) , | response , code | {
2023-07-10 14:01:14 +02:00
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " TetsAB " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" id " : 2 ,
" title " : " TestAB "
} ,
{
" id " : 3 ,
2023-07-12 16:47:30 +02:00
" title " : " TestAb "
} ,
{
" id " : 4 ,
2023-07-10 14:01:14 +02:00
" title " : " testab "
}
]
" ###);
} )
. await ;
}
2023-08-08 16:43:08 +02:00
#[ actix_rt::test ]
async fn simple_search_with_strange_synonyms ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
index . update_settings ( json! ( { " synonyms " : { " & " : [ " to " ] , " to " : [ " & " ] } } ) ) . await ;
let r = index . wait_task ( 0 ) . await ;
meili_snap ::snapshot! ( r [ " status " ] , @ r ### ""succeeded""### ) ;
let documents = DOCUMENTS . clone ( ) ;
index . add_documents ( documents , None ) . await ;
index . wait_task ( 1 ) . await ;
index
. search ( json! ( { " q " : " How to train " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " How to Train Your Dragon: The Hidden World " ,
2023-12-13 21:49:13 +01:00
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100 ,
231 ,
32
]
}
2023-08-08 16:43:08 +02:00
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " How & train " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " How to Train Your Dragon: The Hidden World " ,
2023-12-13 21:49:13 +01:00
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100 ,
231 ,
32
]
}
2023-08-08 16:43:08 +02:00
}
]
" ###);
} )
. await ;
index
. search ( json! ( { " q " : " to " } ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " How to Train Your Dragon: The Hidden World " ,
2023-12-13 21:49:13 +01:00
" id " : " 166428 " ,
" _vectors " : {
" manual " : [
- 100 ,
231 ,
32
]
}
2023-08-08 16:43:08 +02:00
}
]
" ###);
} )
. await ;
}