@@ -296,3 +296,202 @@ fn iterative_facet_string_ordered_iter<'t>(
296296
297297 Ok ( vec. into_iter ( ) )
298298}
299+
300+ #[ cfg( test) ]
301+ mod tests {
302+ use std:: str:: FromStr ;
303+
304+ use big_s:: S ;
305+ use maplit:: hashset;
306+
307+ use crate :: index:: tests:: TempIndex ;
308+ use crate :: { AscDesc , Filter , Search , SearchResult } ;
309+
310+ // Note that in this test, only the iterative sort algorithms are used. Set the CANDIDATES_THESHOLD
311+ // constant to 0 to ensure that the other sort algorithms are also correct.
312+ #[ test]
313+ fn sort_criterion_placeholder ( ) {
314+ let index = TempIndex :: new ( ) ;
315+
316+ index
317+ . update_settings ( |settings| {
318+ settings. set_primary_key ( "id" . to_owned ( ) ) ;
319+ settings
320+ . set_sortable_fields ( maplit:: hashset! { S ( "id" ) , S ( "mod_10" ) , S ( "mod_20" ) } ) ;
321+ settings. set_criteria ( vec ! [ "sort" . to_owned( ) ] ) ;
322+ } )
323+ . unwrap ( ) ;
324+
325+ let mut docs = vec ! [ ] ;
326+ for i in 0 ..100 {
327+ docs. push (
328+ serde_json:: json!( { "id" : i, "mod_10" : format!( "{}" , i % 10 ) , "mod_20" : i % 20 } ) ,
329+ ) ;
330+ }
331+
332+ index. add_documents ( documents ! ( docs) ) . unwrap ( ) ;
333+
334+ let all_ids = ( 0 ..100 ) . collect :: < Vec < _ > > ( ) ;
335+
336+ let rtxn = index. read_txn ( ) . unwrap ( ) ;
337+
338+ let mut search = Search :: new ( & rtxn, & index) ;
339+ search. sort_criteria ( vec ! [ AscDesc :: from_str( "mod_10:desc" ) . unwrap( ) ] ) ;
340+ search. limit ( 100 ) ;
341+
342+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
343+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[9, 19, 29, 39, 49, 59, 69, 79, 89, 99, 8, 18, 28, 38, 48, 58, 68, 78, 88, 98, 7, 17, 27, 37, 47, 57, 67, 77, 87, 97, 6, 16, 26, 36, 46, 56, 66, 76, 86, 96, 5, 15, 25, 35, 45, 55, 65, 75, 85, 95, 4, 14, 24, 34, 44, 54, 64, 74, 84, 94, 3, 13, 23, 33, 43, 53, 63, 73, 83, 93, 2, 12, 22, 32, 42, 52, 62, 72, 82, 92, 1, 11, 21, 31, 41, 51, 61, 71, 81, 91, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90]" ) ;
344+ documents_ids. sort ( ) ;
345+ assert_eq ! ( all_ids, documents_ids) ;
346+
347+ let mut search = Search :: new ( & rtxn, & index) ;
348+ search. sort_criteria ( vec ! [
349+ AscDesc :: from_str( "mod_10:desc" ) . unwrap( ) ,
350+ AscDesc :: from_str( "id:desc" ) . unwrap( ) ,
351+ ] ) ;
352+ search. limit ( 100 ) ;
353+
354+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
355+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[99, 89, 79, 69, 59, 49, 39, 29, 19, 9, 98, 88, 78, 68, 58, 48, 38, 28, 18, 8, 97, 87, 77, 67, 57, 47, 37, 27, 17, 7, 96, 86, 76, 66, 56, 46, 36, 26, 16, 6, 95, 85, 75, 65, 55, 45, 35, 25, 15, 5, 94, 84, 74, 64, 54, 44, 34, 24, 14, 4, 93, 83, 73, 63, 53, 43, 33, 23, 13, 3, 92, 82, 72, 62, 52, 42, 32, 22, 12, 2, 91, 81, 71, 61, 51, 41, 31, 21, 11, 1, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0]" ) ;
356+ documents_ids. sort ( ) ;
357+ assert_eq ! ( all_ids, documents_ids) ;
358+
359+ let mut search = Search :: new ( & rtxn, & index) ;
360+ search. sort_criteria ( vec ! [
361+ AscDesc :: from_str( "mod_10:desc" ) . unwrap( ) ,
362+ AscDesc :: from_str( "mod_20:asc" ) . unwrap( ) ,
363+ ] ) ;
364+ search. limit ( 100 ) ;
365+
366+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
367+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[9, 29, 49, 69, 89, 19, 39, 59, 79, 99, 8, 28, 48, 68, 88, 18, 38, 58, 78, 98, 7, 27, 47, 67, 87, 17, 37, 57, 77, 97, 6, 26, 46, 66, 86, 16, 36, 56, 76, 96, 5, 25, 45, 65, 85, 15, 35, 55, 75, 95, 4, 24, 44, 64, 84, 14, 34, 54, 74, 94, 3, 23, 43, 63, 83, 13, 33, 53, 73, 93, 2, 22, 42, 62, 82, 12, 32, 52, 72, 92, 1, 21, 41, 61, 81, 11, 31, 51, 71, 91, 0, 20, 40, 60, 80, 10, 30, 50, 70, 90]" ) ;
368+ documents_ids. sort ( ) ;
369+ assert_eq ! ( all_ids, documents_ids) ;
370+
371+ let mut search = Search :: new ( & rtxn, & index) ;
372+ search. sort_criteria ( vec ! [
373+ AscDesc :: from_str( "mod_10:desc" ) . unwrap( ) ,
374+ AscDesc :: from_str( "mod_20:desc" ) . unwrap( ) ,
375+ ] ) ;
376+ search. limit ( 100 ) ;
377+
378+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
379+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[19, 39, 59, 79, 99, 9, 29, 49, 69, 89, 18, 38, 58, 78, 98, 8, 28, 48, 68, 88, 17, 37, 57, 77, 97, 7, 27, 47, 67, 87, 16, 36, 56, 76, 96, 6, 26, 46, 66, 86, 15, 35, 55, 75, 95, 5, 25, 45, 65, 85, 14, 34, 54, 74, 94, 4, 24, 44, 64, 84, 13, 33, 53, 73, 93, 3, 23, 43, 63, 83, 12, 32, 52, 72, 92, 2, 22, 42, 62, 82, 11, 31, 51, 71, 91, 1, 21, 41, 61, 81, 10, 30, 50, 70, 90, 0, 20, 40, 60, 80]" ) ;
380+ documents_ids. sort ( ) ;
381+ assert_eq ! ( all_ids, documents_ids) ;
382+
383+ let mut search = Search :: new ( & rtxn, & index) ;
384+ search. sort_criteria ( vec ! [
385+ AscDesc :: from_str( "mod_10:desc" ) . unwrap( ) ,
386+ AscDesc :: from_str( "mod_20:desc" ) . unwrap( ) ,
387+ AscDesc :: from_str( "id:desc" ) . unwrap( ) ,
388+ ] ) ;
389+ search. limit ( 100 ) ;
390+
391+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
392+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[99, 79, 59, 39, 19, 89, 69, 49, 29, 9, 98, 78, 58, 38, 18, 88, 68, 48, 28, 8, 97, 77, 57, 37, 17, 87, 67, 47, 27, 7, 96, 76, 56, 36, 16, 86, 66, 46, 26, 6, 95, 75, 55, 35, 15, 85, 65, 45, 25, 5, 94, 74, 54, 34, 14, 84, 64, 44, 24, 4, 93, 73, 53, 33, 13, 83, 63, 43, 23, 3, 92, 72, 52, 32, 12, 82, 62, 42, 22, 2, 91, 71, 51, 31, 11, 81, 61, 41, 21, 1, 90, 70, 50, 30, 10, 80, 60, 40, 20, 0]" ) ;
393+ documents_ids. sort ( ) ;
394+ assert_eq ! ( all_ids, documents_ids) ;
395+ }
396+
397+ // Note that in this test, only the iterative sort algorithms are used. Set the CANDIDATES_THESHOLD
398+ // constant to 0 to ensure that the other sort algorithms are also correct.
399+ #[ test]
400+ fn sort_criterion_non_placeholder ( ) {
401+ let index = TempIndex :: new ( ) ;
402+
403+ index
404+ . update_settings ( |settings| {
405+ settings. set_primary_key ( "id" . to_owned ( ) ) ;
406+ settings. set_filterable_fields ( hashset ! { S ( "id" ) , S ( "mod_10" ) , S ( "mod_20" ) } ) ;
407+ settings. set_sortable_fields ( hashset ! { S ( "id" ) , S ( "mod_10" ) , S ( "mod_20" ) } ) ;
408+ settings. set_criteria ( vec ! [ "sort" . to_owned( ) ] ) ;
409+ } )
410+ . unwrap ( ) ;
411+
412+ let mut docs = vec ! [ ] ;
413+ for i in 0 ..100 {
414+ docs. push (
415+ serde_json:: json!( { "id" : i, "mod_10" : format!( "{}" , i % 10 ) , "mod_20" : i % 20 } ) ,
416+ ) ;
417+ }
418+
419+ index. add_documents ( documents ! ( docs) ) . unwrap ( ) ;
420+
421+ let rtxn = index. read_txn ( ) . unwrap ( ) ;
422+
423+ let mut search = Search :: new ( & rtxn, & index) ;
424+ search. filter (
425+ Filter :: from_str ( "mod_10 IN [1, 0, 2] OR mod_20 IN [10, 13] OR id IN [5, 6]" )
426+ . unwrap ( )
427+ . unwrap ( ) ,
428+ ) ;
429+ search. sort_criteria ( vec ! [
430+ AscDesc :: from_str( "mod_10:desc" ) . unwrap( ) ,
431+ AscDesc :: from_str( "mod_20:asc" ) . unwrap( ) ,
432+ AscDesc :: from_str( "id:desc" ) . unwrap( ) ,
433+ ] ) ;
434+ search. limit ( 100 ) ;
435+
436+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
437+ // The order should be in increasing value of the id modulo 10, followed by increasing value of the id modulo 20, followed by decreasing value of the id
438+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[6, 5, 93, 73, 53, 33, 13, 82, 62, 42, 22, 2, 92, 72, 52, 32, 12, 81, 61, 41, 21, 1, 91, 71, 51, 31, 11, 80, 60, 40, 20, 0, 90, 70, 50, 30, 10]" ) ;
439+ let expected_ids = ( 0 ..100 )
440+ . filter ( |id| {
441+ [ 1 , 0 , 2 ] . contains ( & ( id % 10 ) )
442+ || [ 10 , 13 ] . contains ( & ( id % 20 ) )
443+ || [ 5 , 6 ] . contains ( id)
444+ } )
445+ . collect :: < Vec < _ > > ( ) ;
446+ documents_ids. sort ( ) ;
447+ assert_eq ! ( expected_ids, documents_ids) ;
448+
449+ let mut search = Search :: new ( & rtxn, & index) ;
450+ search. filter (
451+ Filter :: from_str ( "mod_10 IN [7, 8, 0] OR mod_20 IN [1, 15, 16] OR id IN [0, 4]" )
452+ . unwrap ( )
453+ . unwrap ( ) ,
454+ ) ;
455+ search. sort_criteria ( vec ! [
456+ AscDesc :: from_str( "mod_10:asc" ) . unwrap( ) ,
457+ AscDesc :: from_str( "mod_20:asc" ) . unwrap( ) ,
458+ AscDesc :: from_str( "id:desc" ) . unwrap( ) ,
459+ ] ) ;
460+ search. limit ( 100 ) ;
461+
462+ let SearchResult { mut documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
463+ // The order should be in increasing value of the id modulo 10, followed by increasing value of the id modulo 20, followed by decreasing value of the id
464+ insta:: assert_snapshot!( format!( "{documents_ids:?}" ) , @"[80, 60, 40, 20, 0, 90, 70, 50, 30, 10, 81, 61, 41, 21, 1, 4, 95, 75, 55, 35, 15, 96, 76, 56, 36, 16, 87, 67, 47, 27, 7, 97, 77, 57, 37, 17, 88, 68, 48, 28, 8, 98, 78, 58, 38, 18]" ) ;
465+ let expected_ids = ( 0 ..100 )
466+ . filter ( |id| {
467+ [ 7 , 8 , 0 ] . contains ( & ( id % 10 ) )
468+ || [ 1 , 15 , 16 ] . contains ( & ( id % 20 ) )
469+ || [ 0 , 4 ] . contains ( id)
470+ } )
471+ . collect :: < Vec < _ > > ( ) ;
472+ documents_ids. sort ( ) ;
473+ assert_eq ! ( expected_ids, documents_ids) ;
474+
475+ let mut search = Search :: new ( & rtxn, & index) ;
476+ search. filter (
477+ Filter :: from_str ( "mod_10 IN [1, 0, 2] OR mod_20 IN [10, 13] OR id IN [5, 6]" )
478+ . unwrap ( )
479+ . unwrap ( ) ,
480+ ) ;
481+ search. sort_criteria ( vec ! [ AscDesc :: from_str( "id:desc" ) . unwrap( ) ] ) ;
482+ search. limit ( 100 ) ;
483+
484+ let SearchResult { documents_ids, .. } = search. execute ( ) . unwrap ( ) ;
485+ // The order should be in decreasing value of the id
486+ let mut expected_ids = ( 0 ..100 )
487+ . filter ( |id| {
488+ [ 1 , 0 , 2 ] . contains ( & ( id % 10 ) )
489+ || [ 10 , 13 ] . contains ( & ( id % 20 ) )
490+ || [ 5 , 6 ] . contains ( id)
491+ } )
492+ . collect :: < Vec < _ > > ( ) ;
493+ expected_ids. sort ( ) ;
494+ expected_ids. reverse ( ) ;
495+ assert_eq ! ( expected_ids, documents_ids) ;
496+ }
497+ }
0 commit comments