@@ -2,12 +2,16 @@ use core::fmt;
22use std:: cmp:: min;
33use std:: collections:: { BTreeMap , BTreeSet , HashSet } ;
44use std:: ops:: Not as _;
5+ use std:: rc:: Rc ;
56use std:: str:: FromStr ;
67use std:: sync:: Arc ;
78use std:: time:: Instant ;
89
910use deserr:: Deserr ;
10- use index_scheduler:: filter:: { filter_into_index_filter, parse_filter} ;
11+ use index_scheduler:: filter:: {
12+ filter_into_index_filter, filters_into_index_filters, parse_filter,
13+ retrieve_foreign_keys_settings, SourceIndexUid ,
14+ } ;
1115use index_scheduler:: { IndexScheduler , RoFeatures } ;
1216use indexmap:: IndexMap ;
1317use meilisearch_auth:: IndexSearchRules ;
@@ -23,8 +27,8 @@ use meilisearch_types::milli::score_details::{ScoreDetails, ScoringStrategy};
2327use meilisearch_types:: milli:: vector:: parsed_vectors:: ExplicitVectors ;
2428use meilisearch_types:: milli:: vector:: Embedder ;
2529use meilisearch_types:: milli:: {
26- AttributeState , Deadline , FacetValueHit , IndexFilter , InternalError , OrderBy , PatternMatch ,
27- SearchForFacetValues , SearchStep ,
30+ filtered_universe , AttributeState , Deadline , FacetValueHit , Filter , IndexFilter , InternalError ,
31+ OrderBy , PatternMatch , SearchForFacetValues , SearchStep ,
2832} ;
2933use meilisearch_types:: network:: Network ;
3034use meilisearch_types:: settings:: DEFAULT_PAGINATION_MAX_TOTAL_HITS ;
@@ -2481,6 +2485,7 @@ pub fn perform_similar(
24812485 index_uid : IndexUid ,
24822486 query : SimilarQuery ,
24832487 progress : & Progress ,
2488+ search_rules : Option < IndexSearchRules > ,
24842489) -> Result < SimilarResult , ResponseError > {
24852490 let before_search = Instant :: now ( ) ;
24862491 let features = index_scheduler. features ( ) ;
@@ -2512,6 +2517,30 @@ pub fn perform_similar(
25122517 Route :: Similar ,
25132518 ) ?;
25142519
2520+ let docid_filter = search_rules. and_then ( |search_rules| search_rules. filter ) ;
2521+ let docid_filter = docid_filter
2522+ . as_ref ( )
2523+ . map ( |docid_filter| {
2524+ parse_filter (
2525+ docid_filter,
2526+ Code :: InvalidSimilarFilter ,
2527+ features,
2528+ Some ( index_uid. as_str ( ) ) ,
2529+ )
2530+ } )
2531+ . transpose ( ) ?
2532+ . flatten ( ) ;
2533+
2534+ let candidates_filter = filter
2535+ . as_ref ( )
2536+ . and_then ( |filter| {
2537+ parse_filter ( filter, Code :: InvalidSimilarFilter , features, None ) . transpose ( )
2538+ } )
2539+ . transpose ( ) ?;
2540+
2541+ let ( docid_filter, candidates_filter) =
2542+ extract_filters ( index_scheduler, index_uid, progress, docid_filter, candidates_filter) ?;
2543+
25152544 let id: ExternalDocumentId = id. try_into ( ) . map_err ( |error| {
25162545 let msg = format ! ( "Invalid value at `.id`: {error}" ) ;
25172546 ResponseError :: from_msg ( msg, Code :: InvalidSimilarId )
@@ -2526,6 +2555,14 @@ pub fn perform_similar(
25262555 ) ) ;
25272556 } ;
25282557
2558+ let docid_universe = filtered_universe ( & index, & rtxn, & docid_filter, progress) ?;
2559+ if docid_universe. contains ( internal_id) . not ( ) {
2560+ return Err ( ResponseError :: from_msg (
2561+ MeilisearchHttpError :: DocumentNotFound ( id. into_inner ( ) ) . to_string ( ) ,
2562+ Code :: NotFoundSimilarId ,
2563+ ) ) ;
2564+ }
2565+
25292566 let mut similar = milli:: Similar :: new (
25302567 internal_id,
25312568 offset,
@@ -2538,18 +2575,8 @@ pub fn perform_similar(
25382575 progress,
25392576 ) ;
25402577
2541- if let Some ( ref filter) = filter {
2542- if let Some ( filter) = parse_filter ( filter, Code :: InvalidSimilarFilter , features, None ) ? {
2543- let filter = filter_into_index_filter (
2544- filter,
2545- & index,
2546- & rtxn,
2547- index_scheduler,
2548- progress,
2549- & index_uid,
2550- ) ?;
2551- similar. filter ( filter) ;
2552- }
2578+ if let Some ( filter) = candidates_filter {
2579+ similar. filter ( filter) ;
25532580 }
25542581
25552582 if let Some ( ranking_score_threshold) = ranking_score_threshold {
@@ -2618,6 +2645,33 @@ pub fn perform_similar(
26182645 Ok ( result)
26192646}
26202647
2648+ fn extract_filters < ' a > (
2649+ index_scheduler : & IndexScheduler ,
2650+ index_uid : IndexUid ,
2651+ progress : & Progress ,
2652+ docid_filter : Option < Filter < ' a > > ,
2653+ candidates_filter : Option < Filter < ' a > > ,
2654+ ) -> Result < ( Option < IndexFilter < ' a > > , Option < IndexFilter < ' a > > ) , ResponseError > {
2655+ let source_index_uid = SourceIndexUid ( Rc :: from ( & * index_uid) ) ;
2656+ let foreign_keys_settings =
2657+ retrieve_foreign_keys_settings ( index_scheduler, std:: iter:: once ( & source_index_uid) ) ?;
2658+ let ( docid_filter, candidates_filter) = match filters_into_index_filters (
2659+ vec ! [
2660+ ( source_index_uid. clone( ) , docid_filter) ,
2661+ ( source_index_uid. clone( ) , candidates_filter) ,
2662+ ] ,
2663+ & foreign_keys_settings,
2664+ index_scheduler,
2665+ progress,
2666+ ) ?
2667+ . as_mut_slice ( )
2668+ {
2669+ [ docid_filter, candidates_filter] => ( docid_filter. take ( ) , candidates_filter. take ( ) ) ,
2670+ _ => unreachable ! ( ) ,
2671+ } ;
2672+ Ok ( ( docid_filter, candidates_filter) )
2673+ }
2674+
26212675pub fn insert_geo_distance ( sorts : & [ String ] , document : & mut Document ) {
26222676 lazy_static:: lazy_static! {
26232677 static ref GEO_REGEX : Regex =
0 commit comments