Skip to content

Commit 6a4ea9f

Browse files
author
Daniele Briggi
committed
feat(cli): rebuild/reset commands
1 parent 842bfce commit 6a4ea9f

4 files changed

Lines changed: 314 additions & 80 deletions

File tree

src/sqlite_rag/cli.py

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,53 @@ def remove(
114114

115115

116116
@app.command()
117-
def rebuild():
117+
def rebuild(
118+
remove_missing: bool = typer.Option(
119+
False, "--remove-missing", help="Remove documents whose files are not found"
120+
)
121+
):
118122
"""Rebuild embeddings and full-text index"""
119-
pass
123+
rag = SQLiteRag()
124+
125+
typer.echo("Starting rebuild process...")
126+
127+
result = rag.rebuild(remove_missing=remove_missing)
128+
129+
typer.echo(f"Rebuild completed:")
130+
typer.echo(f" Total documents: {result['total']}")
131+
typer.echo(f" Reprocessed: {result['reprocessed']}")
132+
typer.echo(f" Not found: {result['not_found']}")
133+
typer.echo(f" Removed: {result['removed']}")
120134

121135

122136
@app.command()
123-
def reset():
137+
def reset(
138+
yes: bool = typer.Option(False, "-y", "--yes", help="Skip confirmation prompt")
139+
):
124140
"""Reset/clear the entire database"""
125-
pass
141+
rag = SQLiteRag()
142+
143+
# Show warning and ask for confirmation unless -y flag is used
144+
if not yes:
145+
typer.echo(
146+
"WARNING: This will permanently delete all documents and data from the database!"
147+
)
148+
typer.echo(f"Database file: {rag.settings.db_path}")
149+
typer.echo()
150+
confirm = typer.confirm("Are you sure you want to reset the entire database?")
151+
if not confirm:
152+
typer.echo("Reset cancelled.")
153+
return
154+
155+
typer.echo("Resetting database...")
156+
157+
success = rag.reset()
158+
159+
if success:
160+
typer.echo("Database reset completed successfully.")
161+
else:
162+
typer.echo("Failed to reset database.")
163+
raise typer.Exit(1)
126164

127165

128166
@app.command()
@@ -138,7 +176,14 @@ def search(
138176
return
139177

140178
typer.echo(f"Found {len(results)} documents:")
141-
179+
# print the position, the snippet and the uri as a table
180+
typer.echo(f"{'Position':<10} {'Content':<50}")
181+
typer.echo("-" * 60)
182+
for i, doc in enumerate(results, start=1):
183+
uri_or_content = doc.uri or (
184+
doc.content[:47] + "..." if len(doc.content) > 47 else doc.content
185+
)
186+
typer.echo(f"{i:<10} {uri_or_content:<50}")
142187

143188

144189
def repl_mode():
@@ -147,7 +192,7 @@ def repl_mode():
147192

148193
while True:
149194
try:
150-
command = input("sqlite-rag> ").strip()
195+
command = input("\nsqlite-rag> ").strip()
151196
if not command:
152197
continue
153198

src/sqlite_rag/sqliterag.py

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,96 @@ def remove_document(self, identifier: str) -> bool:
102102

103103
return self._repository.remove_document(document.id or "")
104104

105-
def search(
106-
self, query: str, top_k: int = 10
107-
) -> list[Document]:
105+
def rebuild(self, remove_missing: bool = False) -> dict:
106+
"""Rebuild embeddings and full-text index for all documents"""
107+
self._ensure_initialized()
108+
109+
documents = self._repository.list_documents()
110+
total_docs = len(documents)
111+
reprocessed = 0
112+
not_found = 0
113+
removed = 0
114+
115+
for doc in documents:
116+
if doc.uri and Path(doc.uri).exists():
117+
# File still exists, recreate embeddings
118+
try:
119+
content = FileReader.parse_file(Path(doc.uri))
120+
doc.content = content
121+
122+
self._repository.remove_document(doc.id or "")
123+
processed_doc = self._engine.process(doc)
124+
self._repository.add_document(processed_doc)
125+
126+
reprocessed += 1
127+
self._logger.debug(f"Reprocessed: {doc.uri}")
128+
except Exception as e:
129+
self._logger.error(f"Error processing {doc.uri}: {e}")
130+
not_found += 1
131+
elif doc.uri:
132+
# File not found
133+
not_found += 1
134+
self._logger.warning(f"File not found: {doc.uri}")
135+
136+
if remove_missing:
137+
self._repository.remove_document(doc.id or "")
138+
removed += 1
139+
self._logger.info(f"Removed missing document: {doc.uri}")
140+
else:
141+
# Document without URI (text content)
142+
try:
143+
self._repository.remove_document(doc.id or "")
144+
processed_doc = self._engine.process(doc)
145+
self._repository.add_document(processed_doc)
146+
147+
reprocessed += 1
148+
self._logger.debug(f"Reprocessed text document: {doc.content[:20]!r}...")
149+
except Exception as e:
150+
self._logger.error(f"Error processing text document {doc.id}: {e}")
151+
152+
if self.settings.quantize_scan:
153+
self._engine.quantize()
154+
155+
return {
156+
"total": total_docs,
157+
"reprocessed": reprocessed,
158+
"not_found": not_found,
159+
"removed": removed,
160+
}
161+
162+
def reset(self) -> bool:
163+
"""Reset/clear the entire database by deleting and recreating it"""
164+
db_path = self.settings.db_path
165+
166+
try:
167+
# Close the database connection
168+
self._conn.close()
169+
170+
# Delete the database file if it exists
171+
if Path(db_path).exists():
172+
Path(db_path).unlink()
173+
self._logger.info(f"Deleted database file: {db_path}")
174+
175+
# Recreate the database connection and initialize
176+
self._conn = sqlite3.connect(db_path)
177+
Database.initialize(self._conn, self.settings)
178+
179+
# Reinitialize components with new connection
180+
self._repository = Repository(self._conn, self.settings)
181+
self._chunker = Chunker(self._conn, self.settings)
182+
self._engine = Engine(self._conn, self.settings, chunker=self._chunker)
183+
184+
# Reset ready flag so initialization happens on next use
185+
self.ready = False
186+
187+
self._logger.info("Database reset completed successfully")
188+
return True
189+
190+
except Exception as e:
191+
self._logger.error(f"Error during database reset: {e}")
192+
return False
193+
194+
def search(self, query: str, top_k: int = 10) -> list[Document]:
108195
"""Search for documents matching the query"""
109196
self._ensure_initialized()
110197

test_chunker.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

0 commit comments

Comments
 (0)