perf(#12): push suggest() filtering + LIMIT to SQL
Replace the in-memory approach (list_files → filter in Rust) with SQL-level ILIKE + relevance ORDER BY + LIMIT. - Add suggest_files_by_name() to FileReadPort (default impl for stubs) - Add suggest_folders_by_name() to FolderRepository (default impl) - Implement efficient SQL in FileBlobReadRepository & FolderDbRepository with LOWER(name) LIKE pattern, 3-tier relevance sorting, and LIMIT - Rewrite SearchService::suggest() to call new methods in parallel Before: 50K files → ~10 MB transferred, 52K string comparisons After: 50K files → ~20 rows transferred, index-assisted scan
This commit is contained in:
@@ -125,6 +125,29 @@ pub trait FileReadPort: Send + Sync + 'static {
|
||||
criteria: &SearchCriteriaDto,
|
||||
user_id: &str,
|
||||
) -> Result<usize, DomainError>;
|
||||
|
||||
/// Return up to `limit` files whose name contains `query` (case-insensitive).
|
||||
///
|
||||
/// Results are ordered by relevance (exact > starts-with > contains) so the
|
||||
/// caller can use them directly for autocomplete suggestions.
|
||||
///
|
||||
/// The default implementation falls back to `list_files` + in-memory filter
|
||||
/// so that stubs and mocks compile without changes.
|
||||
async fn suggest_files_by_name(
|
||||
&self,
|
||||
folder_id: Option<&str>,
|
||||
query: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<File>, DomainError> {
|
||||
let all = self.list_files(folder_id).await?;
|
||||
let q = query.to_lowercase();
|
||||
let mut matched: Vec<File> = all
|
||||
.into_iter()
|
||||
.filter(|f| f.name().to_lowercase().contains(&q))
|
||||
.collect();
|
||||
matched.truncate(limit);
|
||||
Ok(matched)
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user