@@ -409,20 +409,12 @@ impl WitnessCorpus {
409409 evicted
410410 }
411411
412- /// Top -k query by SPO.
412+ /// Exact-match top -k query by SPO.
413413 ///
414- /// With `with-cam-pq` ON and cam_pq_state Some: distance-ranked via CAM-PQ ADC.
415- /// Otherwise: HashMap chain-order fallback.
414+ /// Always returns entries whose `spo` exactly equals the query; never
415+ /// returns unrelated neighbours. For distance-ranked nearest-neighbour
416+ /// search, use `query_similar` (feature `with-cam-pq`).
416417 pub fn cam_pq_search ( & self , spo : u64 , k : usize ) -> Vec < & WitnessEntry > {
417- #[ cfg( feature = "with-cam-pq" ) ]
418- if let Some ( state) = self . cam_pq_state . as_ref ( ) {
419- return state
420- . index
421- . cam_pq_search ( spo, k)
422- . into_iter ( )
423- . filter_map ( |( pos, _dist) | self . entries . get ( pos) )
424- . collect ( ) ;
425- }
426418 self . cam_pq_index
427419 . lookup ( spo)
428420 . iter ( )
@@ -979,6 +971,39 @@ mod tests {
979971 assert_eq ! ( idx. len( ) , 0 ) ;
980972 }
981973
974+ // Codex P2 (PR #396): cam_pq_search must remain exact-match even when
975+ // CAM-PQ is enabled. ANN search lives on query_similar. Asserts that a
976+ // missing SPO yields an empty result (not k unrelated neighbours) and
977+ // that an existing SPO yields only its own entries.
978+ #[ cfg( feature = "with-cam-pq" ) ]
979+ #[ test]
980+ fn cam_pq_search_exact_match_when_cam_pq_enabled ( ) {
981+ let mut corpus = WitnessCorpus :: new ( ) ;
982+ let spo_a = 0xAAAA_u64 ;
983+ let spo_b = 0xBBBB_u64 ;
984+ for ts in [ 100u64 , 200 , 300 ] {
985+ corpus. insert ( make_entry ( spo_a, ts) ) ;
986+ }
987+ for ts in [ 10u64 , 20 , 30 ] {
988+ corpus. insert ( make_entry ( spo_b, ts) ) ;
989+ }
990+ corpus. enable_cam_pq ( tiny_codebook ( ) , 0xCAFE_BABE ) ;
991+
992+ let hits_a = corpus. cam_pq_search ( spo_a, 5 ) ;
993+ assert_eq ! ( hits_a. len( ) , 3 , "must return exactly the 3 spo_a entries" ) ;
994+ assert ! (
995+ hits_a. iter( ) . all( |e| e. spo == spo_a) ,
996+ "no unrelated neighbours allowed"
997+ ) ;
998+
999+ let missing = corpus. cam_pq_search ( 0xCCCC_u64 , 5 ) ;
1000+ assert ! (
1001+ missing. is_empty( ) ,
1002+ "missing SPO must return empty, not k nearest neighbours; got {} entries" ,
1003+ missing. len( )
1004+ ) ;
1005+ }
1006+
9821007 // T12: feature OFF smoke — WitnessCorpus::new() and query() work.
9831008 // (Compiles and runs under both feature flags; tests only HashMap path.)
9841009 #[ test]
0 commit comments