feat(search): if the search is empty, return all notes
Some checks are pending
Checks / main (push) Waiting to run

This commit is contained in:
perf3ct 2025-11-04 11:59:41 -08:00
parent 16912e606e
commit 052e28ab1b
No known key found for this signature in database
GPG Key ID: 569C4EEC436F5232
2 changed files with 74 additions and 1 deletions

View File

@ -78,8 +78,13 @@ class NoteContentFulltextExp extends Expression {
const resultNoteSet = new NoteSet();
// Skip FTS5 for empty token searches - traditional search is more efficient
// Empty tokens means we're returning all notes (no filtering), which FTS5 doesn't optimize
if (this.tokens.length === 0) {
// Fall through to traditional search below
}
// Try to use FTS5 if available for better performance
if (ftsSearchService.checkFTS5Availability() && this.canUseFTS5()) {
else if (ftsSearchService.checkFTS5Availability() && this.canUseFTS5()) {
try {
// Check if we need to search protected notes
const searchProtected = protectedSessionService.isProtectedSessionAvailable();

View File

@ -225,6 +225,40 @@ class FTSSearchService {
throw new FTSNotAvailableError();
}
// Handle empty tokens efficiently - return all notes without running diagnostics
if (tokens.length === 0) {
// Empty query means return all indexed notes (optionally filtered by noteIds)
log.info('[FTS-OPTIMIZATION] Empty token array - returning all indexed notes without diagnostics');
const results: FTSSearchResult[] = [];
let query: string;
const params: any[] = [];
if (noteIds && noteIds.size > 0) {
const nonProtectedNoteIds = this.filterNonProtectedNoteIds(noteIds);
if (nonProtectedNoteIds.length === 0) {
return []; // No non-protected notes to search
}
query = `SELECT noteId, title FROM notes_fts WHERE noteId IN (${nonProtectedNoteIds.map(() => '?').join(',')})`;
params.push(...nonProtectedNoteIds);
} else {
// Return all indexed notes
query = `SELECT noteId, title FROM notes_fts`;
}
for (const row of sql.iterateRows<{ noteId: string; title: string }>(query, params)) {
results.push({
noteId: row.noteId,
title: row.title,
score: 0, // No ranking for empty query
snippet: undefined
});
}
log.info(`[FTS-OPTIMIZATION] Empty token search returned ${results.length} results`);
return results;
}
// Normalize tokens to lowercase for case-insensitive search
const normalizedTokens = tokens.map(t => t.toLowerCase());
@ -458,6 +492,40 @@ class FTSSearchService {
throw new FTSNotAvailableError();
}
// Handle empty tokens efficiently - return all notes without MATCH query
if (tokens.length === 0) {
log.info('[FTS-OPTIMIZATION] Empty token array in searchSync - returning all indexed notes');
// Reuse the empty token logic from searchWithLike
const results: FTSSearchResult[] = [];
let query: string;
const params: any[] = [];
if (noteIds && noteIds.size > 0) {
const nonProtectedNoteIds = this.filterNonProtectedNoteIds(noteIds);
if (nonProtectedNoteIds.length === 0) {
return []; // No non-protected notes to search
}
query = `SELECT noteId, title FROM notes_fts WHERE noteId IN (${nonProtectedNoteIds.map(() => '?').join(',')})`;
params.push(...nonProtectedNoteIds);
} else {
// Return all indexed notes
query = `SELECT noteId, title FROM notes_fts`;
}
for (const row of sql.iterateRows<{ noteId: string; title: string }>(query, params)) {
results.push({
noteId: row.noteId,
title: row.title,
score: 0, // No ranking for empty query
snippet: undefined
});
}
log.info(`[FTS-OPTIMIZATION] Empty token search returned ${results.length} results`);
return results;
}
const {
limit = FTS_CONFIG.DEFAULT_LIMIT,
offset = 0,