@@ -539,16 +539,25 @@ impl<const N: usize, E: Embedder, S: NodeStorage<N>> TextIndex<N, E, S> {
539539 where
540540 I : IntoIterator < Item = ( Vec < u8 > , String ) > ,
541541 {
542- // Drop every chunk in the index. Cheaper than per-doc deletion since
543- // we're rebuilding from scratch anyway.
544- let ids: Vec < Vec < u8 > > = self . inner . entries_snapshot ( ) . keys ( ) . cloned ( ) . collect ( ) ;
545- for id in ids {
546- self . inner . remove ( & id) ;
547- }
548- // Re-insert each doc through the chunker.
542+ let mut rebuilt =
543+ ProximityIndex :: new ( self . inner . storage ( ) . clone ( ) , self . inner . config ( ) . clone ( ) ) ;
549544 for ( id, text) in texts {
550- self . insert ( & id, & text) ?;
545+ let chunks = self . chunker . split ( & text) ;
546+ if chunks. is_empty ( ) {
547+ continue ;
548+ }
549+ for ( idx, chunk_text) in chunks. iter ( ) . enumerate ( ) {
550+ let vec = self . embedder . embed ( chunk_text) ?;
551+ if vec. len ( ) != usize:: from ( self . embedder . dim ( ) ) {
552+ return Err ( TextIndexError :: Embed ( EmbedError :: DimensionMismatch {
553+ expected : self . embedder . dim ( ) ,
554+ got : vec. len ( ) ,
555+ } ) ) ;
556+ }
557+ rebuilt. insert ( make_chunk_id ( & id, idx as u32 ) , vec) ?;
558+ }
551559 }
560+ self . inner = rebuilt;
552561 Ok ( ( ) )
553562 }
554563
@@ -640,7 +649,7 @@ mod tests {
640649
641650 fn embed ( & self , text : & str ) -> Result < Vec < f32 > , EmbedError > {
642651 if text. contains ( self . needle ) {
643- return Err ( EmbedError :: Failure ( "forced insert failure" . to_string ( ) ) ) ;
652+ return Err ( EmbedError :: Failure ( "forced embedding failure" . to_string ( ) ) ) ;
644653 }
645654 self . inner . embed ( text)
646655 }
@@ -675,7 +684,7 @@ mod tests {
675684 idx. insert ( b"doc:1" , "stable text" ) . unwrap ( ) ;
676685
677686 let err = idx. insert ( b"doc:1" , "please fail-insert" ) . unwrap_err ( ) ;
678- assert ! ( err. to_string( ) . contains( "forced insert failure" ) ) ;
687+ assert ! ( err. to_string( ) . contains( "forced embedding failure" ) ) ;
679688
680689 let hits = idx. search ( "stable text" , 1 ) . unwrap ( ) ;
681690 assert_eq ! ( hits[ 0 ] . id, b"doc:1" . to_vec( ) ) ;
@@ -801,6 +810,25 @@ mod tests {
801810 assert_eq ! ( hits[ 0 ] . id, b"c" . to_vec( ) ) ;
802811 }
803812
813+ #[ test]
814+ fn failed_reindex_preserves_existing_documents ( ) {
815+ let storage = InMemoryNodeStorage :: < 32 > :: new ( ) ;
816+ let mut idx = TextIndex :: new (
817+ storage,
818+ TextIndexConfig :: new ( FailsOnNeedleEmbedder :: new ( 8 , "fail-reindex" ) ) ,
819+ ) ;
820+ idx. insert ( b"doc:1" , "stable text" ) . unwrap ( ) ;
821+
822+ let err = idx
823+ . reindex_from_texts ( vec ! [ ( b"doc:1" . to_vec( ) , "please fail-reindex" . to_string( ) ) ] )
824+ . unwrap_err ( ) ;
825+ assert ! ( err. to_string( ) . contains( "forced embedding failure" ) ) ;
826+
827+ let hits = idx. search ( "stable text" , 1 ) . unwrap ( ) ;
828+ assert_eq ! ( hits[ 0 ] . id, b"doc:1" . to_vec( ) ) ;
829+ assert ! ( hits[ 0 ] . score < 1e-4 ) ;
830+ }
831+
804832 #[ test]
805833 fn search_returns_at_most_k ( ) {
806834 let storage = InMemoryNodeStorage :: < 32 > :: new ( ) ;
0 commit comments