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 ;
2024-05-30 12:02:42 +02:00
mod matching_strategy ;
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 ;
2024-03-26 15:56:43 +01:00
mod search_queue ;
2021-07-06 11:54:37 +02:00
2024-07-17 11:13:37 +02:00
use meilisearch ::Opt ;
2021-07-06 11:54:37 +02:00
use once_cell ::sync ::Lazy ;
2024-07-17 11:13:37 +02:00
use tempfile ::TempDir ;
2021-07-06 11:54:37 +02:00
2024-07-17 11:13:37 +02:00
use crate ::common ::{ default_settings , Server , Value } ;
2023-09-11 16:50:53 +02:00
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
}
] )
} ) ;
2024-05-30 11:22:26 +02:00
static SCORE_DOCUMENTS : Lazy < Value > = Lazy ::new ( | | {
json! ( [
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
} ,
{
" title " : " Batman " ,
" id " : " D " ,
} ,
{
" title " : " Badman " ,
" id " : " E " ,
}
] )
} ) ;
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
} ,
] )
} ) ;
2024-07-04 12:40:02 +02:00
static FRUITS_DOCUMENTS : Lazy < Value > = Lazy ::new ( | | {
json! ( [
{
" name " : " Exclusive sale: green apple " ,
" id " : " green-apple-boosted " ,
" BOOST " : true
} ,
{
" name " : " Pear " ,
" id " : " pear " ,
} ,
{
" name " : " Red apple gala " ,
" id " : " red-apple-gala " ,
} ,
{
" name " : " Exclusive sale: Red Tomato " ,
" id " : " red-tomatoes-boosted " ,
" BOOST " : true
} ,
{
" name " : " Exclusive sale: Red delicious apple " ,
" id " : " red-delicious-boosted " ,
" BOOST " : true ,
}
] )
} ) ;
static VECTOR_DOCUMENTS : Lazy < Value > = Lazy ::new ( | | {
json! ( [
{
" id " : " A " ,
" description " : " the dog barks at the cat " ,
" _vectors " : {
// dimensions [canine, feline, young]
" animal " : [ 0.9 , 0.8 , 0.05 ] ,
// dimensions [negative/positive, energy]
" sentiment " : [ - 0.1 , 0.55 ]
}
} ,
{
" id " : " B " ,
" description " : " the kitten scratched the beagle " ,
" _vectors " : {
// dimensions [canine, feline, young]
" animal " : [ 0.8 , 0.9 , 0.5 ] ,
// dimensions [negative/positive, energy]
" sentiment " : [ - 0.2 , 0.65 ]
}
} ,
{
" id " : " C " ,
" description " : " the dog had to stay alone today " ,
" _vectors " : {
// dimensions [canine, feline, young]
" animal " : [ 0.85 , 0.02 , 0.1 ] ,
// dimensions [negative/positive, energy]
" sentiment " : [ - 1.0 , 0.1 ]
}
} ,
{
" id " : " D " ,
" description " : " the little boy pets the puppy " ,
" _vectors " : {
// dimensions [canine, feline, young]
" animal " : [ 0.8 , 0.09 , 0.8 ] ,
// dimensions [negative/positive, energy]
" sentiment " : [ 0.8 , 0.3 ]
}
} ,
] )
} ) ;
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
}
2024-07-25 08:12:27 +02:00
#[ actix_rt::test ]
async fn filter_should_not_return_documents_with_missing_filterable_value ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
let ( _ , code ) = index
. update_settings ( json! ( { " filterableAttributes " : [ " custom_propertie.999.options " ] } ) )
. await ;
meili_snap ::snapshot! ( code , @ " 202 Accepted " ) ;
index
. add_documents (
json! ( [
{
" id " : 951 ,
" custom_propertie " : {
" 999 " : {
" type " : " multi " ,
" options " : [
{
" slug " : " group_1 " ,
" age " : 6 ,
} ,
{
" slug " : " group_2 " ,
" age " : 8 ,
} ,
]
}
} ,
} ] ) ,
None ,
)
. await ;
index . wait_task ( 1 ) . await ;
index
. search (
json! ( { " filter " : " custom_propertie.999.options.slug IN ['group_2'] " } ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} ,
)
. await ;
index
. add_documents (
json! ( [
{
" id " : 951 ,
" custom_propertie " : {
" 999 " : {
" type " : " multi " ,
" options " : [
{
" slug " : " group_1 " ,
" age " : 6 ,
} ,
]
}
} ,
} ] ) ,
None ,
)
. await ;
index . wait_task ( 2 ) . await ;
index
. search (
json! ( { " filter " : " custom_propertie.999.options.slug IN ['group_2'] " } ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 1 ) ;
} ,
)
. 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 ;
}
2024-04-04 12:01:04 +02:00
#[ actix_rt::test ]
async fn negative_phrase_search ( ) {
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 ;
index
. search ( json! ( { " q " : " - \" train your dragon \" " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let hits = response [ " hits " ] . as_array ( ) . unwrap ( ) ;
assert_eq! ( hits . len ( ) , 4 ) ;
assert_eq! ( hits [ 0 ] [ " id " ] , " 287947 " ) ;
assert_eq! ( hits [ 1 ] [ " id " ] , " 299537 " ) ;
assert_eq! ( hits [ 2 ] [ " id " ] , " 522681 " ) ;
assert_eq! ( hits [ 3 ] [ " id " ] , " 450465 " ) ;
} )
. await ;
}
#[ actix_rt::test ]
async fn negative_word_search ( ) {
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 ;
index
. search ( json! ( { " q " : " -escape " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let hits = response [ " hits " ] . as_array ( ) . unwrap ( ) ;
assert_eq! ( hits . len ( ) , 4 ) ;
assert_eq! ( hits [ 0 ] [ " id " ] , " 287947 " ) ;
assert_eq! ( hits [ 1 ] [ " id " ] , " 299537 " ) ;
assert_eq! ( hits [ 2 ] [ " id " ] , " 166428 " ) ;
assert_eq! ( hits [ 3 ] [ " id " ] , " 450465 " ) ;
} )
. await ;
// Everything that contains derivates of escape but not escape: nothing
index
. search ( json! ( { " q " : " -escape escape " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let hits = response [ " hits " ] . as_array ( ) . unwrap ( ) ;
assert_eq! ( hits . len ( ) , 0 ) ;
} )
. await ;
}
#[ actix_rt::test ]
async fn non_negative_search ( ) {
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 ;
index
. search ( json! ( { " q " : " - escape " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let hits = response [ " hits " ] . as_array ( ) . unwrap ( ) ;
assert_eq! ( hits . len ( ) , 1 ) ;
assert_eq! ( hits [ 0 ] [ " id " ] , " 522681 " ) ;
} )
. await ;
index
. search ( json! ( { " q " : " - \" train your dragon \" " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let hits = response [ " hits " ] . as_array ( ) . unwrap ( ) ;
assert_eq! ( hits . len ( ) , 1 ) ;
assert_eq! ( hits [ 0 ] [ " id " ] , " 166428 " ) ;
} )
. await ;
}
#[ actix_rt::test ]
async fn negative_special_cases_search ( ) {
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 ;
2024-06-06 17:24:50 +02:00
index . update_settings ( json! ( { " synonyms " : { " escape " : [ " gläss " ] } } ) ) . await ;
2024-04-04 12:01:04 +02:00
index . wait_task ( 1 ) . await ;
// There is a synonym for escape -> glass but we don't want "escape", only the derivates: glass
index
. search ( json! ( { " q " : " -escape escape " } ) , | response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let hits = response [ " hits " ] . as_array ( ) . unwrap ( ) ;
assert_eq! ( hits . len ( ) , 1 ) ;
assert_eq! ( hits [ 0 ] [ " id " ] , " 450465 " ) ;
} )
. 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 ) ;
}
2024-07-17 11:13:37 +02:00
#[ actix_rt::test ]
async fn search_with_contains_filter ( ) {
let temp = TempDir ::new ( ) . unwrap ( ) ;
let server = Server ::new_with_options ( Opt {
experimental_contains_filter : true ,
.. default_settings ( temp . path ( ) )
} )
. await
. unwrap ( ) ;
let index = server . index ( " movies " ) ;
index . update_settings ( json! ( { " filterableAttributes " : [ " title " ] } ) ) . await ;
let documents = DOCUMENTS . clone ( ) ;
let ( request , _code ) = index . add_documents ( documents , None ) . await ;
index . wait_task ( request . uid ( ) ) . await . succeeded ( ) ;
let ( response , code ) = index
. search_post ( json! ( {
" filter " : " title CONTAINS cap "
} ) )
. await ;
assert_eq! ( code , 200 , " {} " , response ) ;
assert_eq! ( response [ " hits " ] . as_array ( ) . unwrap ( ) . len ( ) , 2 ) ;
}
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 ;
2024-05-22 16:05:55 +02:00
index . update_settings ( json! ( { " filterableAttributes " : [ " doggos.name " ] } ) ) . await ;
index . wait_task ( 5 ) . await ;
index
. search (
json! ( {
" facets " : [ " doggos.name " ]
} ) ,
| response , code | {
assert_eq! ( code , 200 , " {} " , response ) ;
let dist = response [ " facetDistribution " ] . as_object ( ) . unwrap ( ) ;
assert_eq! ( dist . len ( ) , 1 ) ;
assert_eq! (
dist [ " doggos.name " ] ,
json! ( { " bobby " : 1 , " buddy " : 1 , " gros bill " : 1 , " turbo " : 1 , " fast " : 1 } )
) ;
} ,
)
. 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 ]
2024-02-06 10:55:27 +01:00
async fn test_score_details ( ) {
2023-07-06 09:02:02 +02:00
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = DOCUMENTS . clone ( ) ;
2024-02-06 10:55:27 +01:00
let res = index . add_documents ( json! ( documents ) , None ) . await ;
index . wait_task ( res . 0. uid ( ) ) . await ;
2023-07-06 09:02:02 +02:00
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 " : [
2024-05-14 14:14:02 +02:00
- 100.0 ,
231.0 ,
32.0
2023-12-13 21:49:13 +01:00
]
} ,
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 ,
2024-05-16 01:06:33 +02:00
" score " : 0.8095238095238095
2023-07-06 09:02:02 +02:00
} ,
" exactness " : {
" order " : 4 ,
" matchType " : " noExactMatch " ,
" matchingWords " : 2 ,
" maxMatchingWords " : 2 ,
" score " : 0.3333333333333333
}
}
}
]
" ###);
} ,
)
2024-03-14 17:34:46 +01:00
. await ;
}
2024-05-30 11:22:26 +02:00
#[ actix_rt::test ]
async fn test_score ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let res = index . add_documents ( json! ( documents ) , None ) . await ;
index . wait_task ( res . 0. uid ( ) ) . await ;
index
. search (
json! ( {
" q " : " Badman the dark knight returns 1 " ,
" showRankingScore " : true ,
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _rankingScore " : 0.9746605609456898
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _rankingScore " : 0.8055252965383685
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _rankingScore " : 0.16666666666666666
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _rankingScore " : 0.07702020202020202
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _rankingScore " : 0.07702020202020202
}
]
" ###);
} ,
)
. await ;
}
#[ actix_rt::test ]
async fn test_score_threshold ( ) {
let query = " Badman dark returns 1 " ;
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = SCORE_DOCUMENTS . clone ( ) ;
let res = index . add_documents ( json! ( documents ) , None ) . await ;
index . wait_task ( res . 0. uid ( ) ) . await ;
index
. search (
json! ( {
" q " : query ,
" showRankingScore " : true ,
" rankingScoreThreshold " : 0.0
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " estimatedTotalHits " ] ) , @ " 5 " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _rankingScore " : 0.93430081300813
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _rankingScore " : 0.6685627880184332
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _rankingScore " : 0.25
} ,
{
" title " : " Batman Returns " ,
" id " : " C " ,
" _rankingScore " : 0.11553030303030302
} ,
{
" title " : " Batman " ,
" id " : " D " ,
" _rankingScore " : 0.11553030303030302
}
]
" ###);
} ,
)
. await ;
index
. search (
json! ( {
" q " : query ,
" showRankingScore " : true ,
" rankingScoreThreshold " : 0.2
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " estimatedTotalHits " ] ) , @ r ### "3"### ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _rankingScore " : 0.93430081300813
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _rankingScore " : 0.6685627880184332
} ,
{
" title " : " Badman " ,
" id " : " E " ,
" _rankingScore " : 0.25
}
]
" ###);
} ,
)
. await ;
index
. search (
json! ( {
" q " : query ,
" showRankingScore " : true ,
" rankingScoreThreshold " : 0.5
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " estimatedTotalHits " ] ) , @ r ### "2"### ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _rankingScore " : 0.93430081300813
} ,
{
" title " : " Batman the dark knight returns: Part 2 " ,
" id " : " B " ,
" _rankingScore " : 0.6685627880184332
}
]
" ###);
} ,
)
. await ;
index
. search (
json! ( {
" q " : query ,
" showRankingScore " : true ,
" rankingScoreThreshold " : 0.8
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " estimatedTotalHits " ] ) , @ r ### "1"### ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ r ###"
[
{
" title " : " Batman the dark knight returns: Part 1 " ,
" id " : " A " ,
" _rankingScore " : 0.93430081300813
}
]
" ###);
} ,
)
. await ;
index
. search (
json! ( {
" q " : query ,
" showRankingScore " : true ,
" rankingScoreThreshold " : 1.0
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " estimatedTotalHits " ] ) , @ r ### "0"### ) ;
// nobody is perfect
meili_snap ::snapshot! ( meili_snap ::json_string! ( response [ " hits " ] ) , @ " [] " ) ;
} ,
)
. await ;
}
2024-03-14 17:34:46 +01:00
#[ actix_rt::test ]
async fn test_degraded_score_details ( ) {
let server = Server ::new ( ) . await ;
let index = server . index ( " test " ) ;
let documents = NESTED_DOCUMENTS . clone ( ) ;
index . add_documents ( json! ( documents ) , None ) . await ;
// We can't really use anything else than 0ms here; otherwise, the test will get flaky.
2024-03-18 12:06:00 +01:00
let ( res , _code ) = index . update_settings ( json! ( { " searchCutoffMs " : 0 } ) ) . await ;
2024-03-14 17:34:46 +01:00
index . wait_task ( res . uid ( ) ) . await ;
index
. search (
json! ( {
" q " : " b " ,
2024-03-14 17:42:33 +01:00
" attributesToRetrieve " : [ " doggos.name " , " cattos " ] ,
2024-03-14 17:34:46 +01:00
" showRankingScoreDetails " : true ,
} ) ,
| response , code | {
meili_snap ::snapshot! ( code , @ " 200 OK " ) ;
2024-03-18 12:06:00 +01:00
meili_snap ::snapshot! ( meili_snap ::json_string! ( response , { " .processingTimeMs " = > " [duration] " } ) , @ r ###"
2024-03-14 17:52:08 +01:00
{
" hits " : [
{
" doggos " : [
{
" name " : " bobby "
} ,
{
" name " : " buddy "
}
] ,
" cattos " : " pésti " ,
" _rankingScoreDetails " : {
2024-03-19 15:45:04 +01:00
" skipped " : {
" order " : 0
}
2024-03-14 17:34:46 +01:00
}
2024-03-14 17:52:08 +01:00
} ,
{
" doggos " : [
{
" name " : " gros bill "
}
] ,
" cattos " : [
" simba " ,
" pestiféré "
] ,
" _rankingScoreDetails " : {
2024-03-19 15:45:04 +01:00
" skipped " : {
" order " : 0
}
2024-03-14 17:34:46 +01:00
}
2024-03-14 17:52:08 +01:00
} ,
{
" doggos " : [
{
" name " : " turbo "
} ,
{
" name " : " fast "
}
] ,
" cattos " : [
" moumoute " ,
" gomez "
] ,
" _rankingScoreDetails " : {
2024-03-19 15:45:04 +01:00
" skipped " : {
" order " : 0
}
2024-03-14 17:34:46 +01:00
}
}
2024-03-14 17:52:08 +01:00
] ,
" query " : " b " ,
2024-03-18 12:06:00 +01:00
" processingTimeMs " : " [duration] " ,
2024-03-14 17:52:08 +01:00
" limit " : 20 ,
" offset " : 0 ,
" estimatedTotalHits " : 3
}
2024-03-14 17:34:46 +01:00
" ###);
} ,
)
2023-07-06 09:02:02 +02:00
. 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 ;
2024-06-10 18:26:12 +02:00
index
. search ( json! ( {
2023-07-06 09:02:02 +02:00
" vector " : [ 1.0 , 2.0 , 3.0 ] ,
2024-04-03 14:29:17 +02:00
" showRankingScore " : true
2024-06-10 18:26:12 +02:00
} ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 400 Bad Request " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response ) , @ r ###"
{
" message " : " Passing `vector` as a 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 "
}
" ###);
} )
. await ;
index
. search ( json! ( {
" retrieveVectors " : true ,
" showRankingScore " : true
} ) , | response , code | {
meili_snap ::snapshot! ( code , @ " 400 Bad Request " ) ;
meili_snap ::snapshot! ( meili_snap ::json_string! ( response ) , @ r ###"
{
" message " : " Passing `retrieveVectors` as a 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 "
}
" ###);
} )
2023-07-06 09:02:02 +02:00
. await ;
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 " : {
2023-12-20 17:06:50 +01:00
" source " : " userProvided " ,
" dimensions " : 3 ,
2023-12-13 21:49:13 +01:00
}
} } ) )
. await ;
2023-12-20 17:06:50 +01:00
meili_snap ::snapshot! ( response , @ r ###"
{
" taskUid " : 1 ,
" indexUid " : " test " ,
" status " : " enqueued " ,
" type " : " settingsUpdate " ,
" enqueuedAt " : " [date] "
}
" ###);
2023-12-13 21:49:13 +01:00
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 ] ,
2024-04-03 14:29:17 +02:00
" showRankingScore " : true ,
2024-06-03 16:04:14 +02:00
" retrieveVectors " : true ,
2023-07-06 09:02:02 +02:00
} ) )
. 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 " : {
2024-06-04 17:38:28 +02:00
" manual " : {
2024-06-05 16:10:56 +02:00
" embeddings " : [
[
1.0 ,
2.0 ,
3.0
]
] ,
2024-06-12 18:13:34 +02:00
" regenerate " : false
2024-06-04 17:38:28 +02:00
}
2023-12-13 21:49:13 +01:00
} ,
2024-04-03 14:29:17 +02:00
" _rankingScore " : 1.0
2023-12-12 23:39:01 +01:00
} ,
{
" title " : " Captain Marvel " ,
2023-12-13 21:49:13 +01:00
" id " : " 299537 " ,
" _vectors " : {
2024-06-04 17:38:28 +02:00
" manual " : {
2024-06-05 16:10:56 +02:00
" embeddings " : [
[
1.0 ,
2.0 ,
54.0
]
] ,
2024-06-12 18:13:34 +02:00
" regenerate " : false
2024-06-04 17:38:28 +02:00
}
2023-12-13 21:49:13 +01:00
} ,
2024-04-03 14:29:17 +02:00
" _rankingScore " : 0.9129111766815186
2023-12-12 23:39:01 +01:00
} ,
{
2023-12-13 21:49:13 +01:00
" title " : " Gläss " ,
" id " : " 450465 " ,
" _vectors " : {
2024-06-04 17:38:28 +02:00
" manual " : {
2024-06-05 16:10:56 +02:00
" embeddings " : [
[
- 100.0 ,
340.0 ,
90.0
]
] ,
2024-06-12 18:13:34 +02:00
" regenerate " : false
2024-06-04 17:38:28 +02:00
}
2023-12-13 21:49:13 +01:00
} ,
2024-04-03 14:29:17 +02:00
" _rankingScore " : 0.8106412887573242
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 " : {
2024-06-04 17:38:28 +02:00
" manual " : {
2024-06-05 16:10:56 +02:00
" embeddings " : [
[
- 100.0 ,
231.0 ,
32.0
]
] ,
2024-06-12 18:13:34 +02:00
" regenerate " : false
2024-06-04 17:38:28 +02:00
}
2023-12-13 21:49:13 +01:00
} ,
2024-04-03 14:29:17 +02:00
" _rankingScore " : 0.7412010431289673
2023-12-12 23:39:01 +01:00
} ,
{
2023-12-13 21:49:13 +01:00
" title " : " Escape Room " ,
" id " : " 522681 " ,
" _vectors " : {
2024-06-04 17:38:28 +02:00
" manual " : {
2024-06-05 16:10:56 +02:00
" embeddings " : [
[
10.0 ,
- 23.0 ,
32.0
]
] ,
2024-06-12 18:13:34 +02:00
" regenerate " : false
2024-06-04 17:38:28 +02:00
}
2024-04-03 14:29:17 +02:00
} ,
" _rankingScore " : 0.6972063183784485
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 " : [
2024-05-14 14:14:02 +02:00
- 100.0 ,
231.0 ,
32.0
2023-12-13 21:49:13 +01:00
]
}
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 " : [
2024-05-14 14:14:02 +02:00
- 100.0 ,
231.0 ,
32.0
2023-12-13 21:49:13 +01:00
]
}
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 " : [
2024-05-14 14:14:02 +02:00
- 100.0 ,
231.0 ,
32.0
2023-12-13 21:49:13 +01:00
]
}
2023-08-08 16:43:08 +02:00
}
]
" ###);
} )
. await ;
}