Skip to main content

sqlite_graphrag/commands/
forget.rs

1use crate::errors::AppError;
2use crate::i18n::erros;
3use crate::output;
4use crate::paths::AppPaths;
5use crate::storage::connection::open_rw;
6use crate::storage::memories;
7use serde::Serialize;
8
9#[derive(clap::Args)]
10pub struct ForgetArgs {
11    #[arg(long)]
12    pub name: String,
13    #[arg(long, default_value = "global")]
14    pub namespace: Option<String>,
15    #[arg(long, hide = true, help = "No-op; JSON is always emitted on stdout")]
16    pub json: bool,
17    #[arg(long, env = "SQLITE_GRAPHRAG_DB_PATH")]
18    pub db: Option<String>,
19}
20
21#[derive(Serialize)]
22struct ForgetResponse {
23    forgotten: bool,
24    name: String,
25    namespace: String,
26    /// Tempo total de execução em milissegundos desde início do handler até serialização.
27    elapsed_ms: u64,
28}
29
30pub fn run(args: ForgetArgs) -> Result<(), AppError> {
31    let inicio = std::time::Instant::now();
32    let namespace = crate::namespace::resolve_namespace(args.namespace.as_deref())?;
33    let paths = AppPaths::resolve(args.db.as_deref())?;
34
35    let conn = open_rw(&paths.db)?;
36
37    let maybe_row = memories::read_by_name(&conn, &namespace, &args.name)?;
38    let forgotten = memories::soft_delete(&conn, &namespace, &args.name)?;
39
40    if !forgotten {
41        return Err(AppError::NotFound(erros::memoria_nao_encontrada(
42            &args.name, &namespace,
43        )));
44    }
45
46    if let Some(row) = maybe_row {
47        // FTS5 external-content: manual `DELETE FROM fts_memories WHERE rowid=?`
48        // corrompe o índice. A limpeza correta acontece via trigger `trg_fts_ad`
49        // quando `purge` remove fisicamente a linha de `memories`. Entre soft-delete
50        // e purge, as queries FTS filtram `m.deleted_at IS NULL` no JOIN.
51        if let Err(e) = memories::delete_vec(&conn, row.id) {
52            tracing::warn!(memory_id = row.id, error = %e, "vec cleanup failed — orphan vector left");
53        }
54    }
55
56    output::emit_json(&ForgetResponse {
57        forgotten: true,
58        name: args.name,
59        namespace,
60        elapsed_ms: inicio.elapsed().as_millis() as u64,
61    })?;
62
63    Ok(())
64}
65
66#[cfg(test)]
67mod testes {
68    use super::*;
69
70    #[test]
71    fn forget_response_serializa_campos_basicos() {
72        let resp = ForgetResponse {
73            forgotten: true,
74            name: "minha-memoria".to_string(),
75            namespace: "global".to_string(),
76            elapsed_ms: 5,
77        };
78        let json = serde_json::to_value(&resp).expect("serialização falhou");
79        assert_eq!(json["forgotten"], true);
80        assert_eq!(json["name"], "minha-memoria");
81        assert_eq!(json["namespace"], "global");
82        assert!(json["elapsed_ms"].is_number());
83    }
84
85    #[test]
86    fn forget_response_forgotten_true_indica_sucesso() {
87        let resp = ForgetResponse {
88            forgotten: true,
89            name: "teste".to_string(),
90            namespace: "ns".to_string(),
91            elapsed_ms: 1,
92        };
93        assert!(
94            resp.forgotten,
95            "forgotten deve ser true quando soft-delete bem-sucedido"
96        );
97    }
98
99    #[test]
100    fn forget_resposta_com_namespace_correto() {
101        let resp = ForgetResponse {
102            forgotten: true,
103            name: "abc".to_string(),
104            namespace: "meu-projeto".to_string(),
105            elapsed_ms: 0,
106        };
107        assert_eq!(
108            resp.namespace, "meu-projeto",
109            "namespace deve ser preservado na resposta"
110        );
111    }
112
113    #[test]
114    fn forget_elapsed_ms_zero_e_valido() {
115        let resp = ForgetResponse {
116            forgotten: true,
117            name: "qualquer".to_string(),
118            namespace: "global".to_string(),
119            elapsed_ms: 0,
120        };
121        let json = serde_json::to_value(&resp).expect("serialização falhou");
122        assert_eq!(json["elapsed_ms"], 0u64);
123    }
124}