Skip to main content

sqlite_graphrag/storage/memories/
listing.rs

1//! Paginated listing and counting over `memories`.
2
3use super::rows::MemoryRow;
4use crate::errors::AppError;
5use rusqlite::{params, Connection};
6
7/// Lists live memories in a namespace ordered by `updated_at` descending.
8///
9/// # Arguments
10///
11/// - `memory_type` — optional filter on the `type` column.
12/// - `limit` / `offset` — standard pagination controls in rows.
13///
14/// # Errors
15///
16/// Returns `Err(AppError::Database)` on any `rusqlite` failure.
17pub fn list(
18    conn: &Connection,
19    namespace: &str,
20    memory_type: Option<&str>,
21    limit: usize,
22    offset: usize,
23    include_deleted: bool,
24) -> Result<Vec<MemoryRow>, AppError> {
25    if let Some(mt) = memory_type {
26        let sql = if include_deleted {
27            "SELECT id, namespace, name, type, description, body, body_hash,
28                    session_id, source, metadata, created_at, updated_at, deleted_at
29             FROM memories WHERE namespace=?1 AND type=?2
30             ORDER BY updated_at DESC LIMIT ?3 OFFSET ?4"
31        } else {
32            "SELECT id, namespace, name, type, description, body, body_hash,
33                    session_id, source, metadata, created_at, updated_at, deleted_at
34             FROM memories WHERE namespace=?1 AND type=?2 AND deleted_at IS NULL
35             ORDER BY updated_at DESC LIMIT ?3 OFFSET ?4"
36        };
37        let mut stmt = conn.prepare_cached(sql)?;
38        let rows = stmt
39            .query_map(params![namespace, mt, limit as i64, offset as i64], |r| {
40                Ok(MemoryRow {
41                    id: r.get(0)?,
42                    namespace: r.get(1)?,
43                    name: r.get(2)?,
44                    memory_type: r.get(3)?,
45                    description: r.get(4)?,
46                    body: r.get(5)?,
47                    body_hash: r.get(6)?,
48                    session_id: r.get(7)?,
49                    source: r.get(8)?,
50                    metadata: r.get(9)?,
51                    created_at: r.get(10)?,
52                    updated_at: r.get(11)?,
53                    deleted_at: r.get(12)?,
54                })
55            })?
56            .collect::<Result<Vec<_>, _>>()?;
57        Ok(rows)
58    } else {
59        let sql = if include_deleted {
60            "SELECT id, namespace, name, type, description, body, body_hash,
61                    session_id, source, metadata, created_at, updated_at, deleted_at
62             FROM memories WHERE namespace=?1
63             ORDER BY updated_at DESC LIMIT ?2 OFFSET ?3"
64        } else {
65            "SELECT id, namespace, name, type, description, body, body_hash,
66                    session_id, source, metadata, created_at, updated_at, deleted_at
67             FROM memories WHERE namespace=?1 AND deleted_at IS NULL
68             ORDER BY updated_at DESC LIMIT ?2 OFFSET ?3"
69        };
70        let mut stmt = conn.prepare_cached(sql)?;
71        let rows = stmt
72            .query_map(params![namespace, limit as i64, offset as i64], |r| {
73                Ok(MemoryRow {
74                    id: r.get(0)?,
75                    namespace: r.get(1)?,
76                    name: r.get(2)?,
77                    memory_type: r.get(3)?,
78                    description: r.get(4)?,
79                    body: r.get(5)?,
80                    body_hash: r.get(6)?,
81                    session_id: r.get(7)?,
82                    source: r.get(8)?,
83                    metadata: r.get(9)?,
84                    created_at: r.get(10)?,
85                    updated_at: r.get(11)?,
86                    deleted_at: r.get(12)?,
87                })
88            })?
89            .collect::<Result<Vec<_>, _>>()?;
90        Ok(rows)
91    }
92}
93
94/// Count.
95pub fn count(
96    conn: &Connection,
97    namespace: &str,
98    memory_type: Option<&str>,
99    include_deleted: bool,
100) -> Result<usize, AppError> {
101    let (sql, params_vec): (&str, Vec<Box<dyn rusqlite::types::ToSql>>) = match (
102        memory_type,
103        include_deleted,
104    ) {
105        (Some(mt), true) => (
106            "SELECT COUNT(*) FROM memories WHERE namespace=?1 AND type=?2",
107            vec![
108                Box::new(namespace.to_string()) as Box<dyn rusqlite::types::ToSql>,
109                Box::new(mt.to_string()),
110            ],
111        ),
112        (Some(mt), false) => (
113            "SELECT COUNT(*) FROM memories WHERE namespace=?1 AND type=?2 AND deleted_at IS NULL",
114            vec![
115                Box::new(namespace.to_string()) as Box<dyn rusqlite::types::ToSql>,
116                Box::new(mt.to_string()),
117            ],
118        ),
119        (None, true) => (
120            "SELECT COUNT(*) FROM memories WHERE namespace=?1",
121            vec![Box::new(namespace.to_string()) as Box<dyn rusqlite::types::ToSql>],
122        ),
123        (None, false) => (
124            "SELECT COUNT(*) FROM memories WHERE namespace=?1 AND deleted_at IS NULL",
125            vec![Box::new(namespace.to_string()) as Box<dyn rusqlite::types::ToSql>],
126        ),
127    };
128    let params_refs: Vec<&dyn rusqlite::types::ToSql> =
129        params_vec.iter().map(|b| b.as_ref()).collect();
130    let n: i64 = conn.query_row(sql, params_refs.as_slice(), |r| r.get(0))?;
131    Ok(n as usize)
132}