Merge pull request #154 from DioCrafts/claude/optimize-performance-FbFK2
Add GIN trigram indexes for substring search performance
This commit is contained in:
@@ -532,10 +532,10 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
}
|
||||
|
||||
if let Some(name) = &criteria.name_contains
|
||||
&& !name.is_empty()
|
||||
&& name.len() >= 3
|
||||
{
|
||||
bind_idx += 1;
|
||||
conditions.push(format!("LOWER(fi.name) LIKE ${bind_idx}"));
|
||||
conditions.push(format!("fi.name ILIKE ${bind_idx}"));
|
||||
}
|
||||
|
||||
let where_clause = conditions.join(" AND ");
|
||||
@@ -578,9 +578,9 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
query = query.bind(fid);
|
||||
}
|
||||
if let Some(name) = &criteria.name_contains
|
||||
&& !name.is_empty()
|
||||
&& name.len() >= 3
|
||||
{
|
||||
query = query.bind(format!("%{}%", name.to_lowercase()));
|
||||
query = query.bind(format!("%{}%", name));
|
||||
}
|
||||
query = query.bind(limit).bind(offset);
|
||||
|
||||
@@ -652,10 +652,10 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
);
|
||||
|
||||
if let Some(name) = &criteria.name_contains
|
||||
&& !name.is_empty()
|
||||
&& name.len() >= 3
|
||||
{
|
||||
bind_idx += 1;
|
||||
conditions.push(format!("LOWER(fi.name) LIKE ${bind_idx}"));
|
||||
conditions.push(format!("fi.name ILIKE ${bind_idx}"));
|
||||
}
|
||||
if let Some(types) = &criteria.file_types
|
||||
&& !types.is_empty()
|
||||
@@ -737,9 +737,9 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
.bind(root_id);
|
||||
|
||||
if let Some(name) = &criteria.name_contains
|
||||
&& !name.is_empty()
|
||||
&& name.len() >= 3
|
||||
{
|
||||
query = query.bind(format!("%{}%", name.to_lowercase()));
|
||||
query = query.bind(format!("%{}%", name));
|
||||
}
|
||||
if let Some(types) = &criteria.file_types
|
||||
&& !types.is_empty()
|
||||
@@ -807,9 +807,8 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
query: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<File>, DomainError> {
|
||||
let pattern = format!("%{}%", query.to_lowercase());
|
||||
let pattern = format!("%{}%", query);
|
||||
let limit_i64 = limit as i64;
|
||||
let query_lower = query.to_lowercase();
|
||||
|
||||
let rows: Vec<(
|
||||
String,
|
||||
@@ -833,10 +832,10 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
LEFT JOIN storage.folders fo ON fo.id = fi.folder_id
|
||||
WHERE fi.folder_id = $1::uuid
|
||||
AND NOT fi.is_trashed
|
||||
AND LOWER(fi.name) LIKE $2
|
||||
AND fi.name ILIKE $2
|
||||
ORDER BY CASE
|
||||
WHEN LOWER(fi.name) = $3 THEN 0
|
||||
WHEN LOWER(fi.name) LIKE $3 || '%' THEN 1
|
||||
WHEN fi.name ILIKE $3 THEN 0
|
||||
WHEN fi.name ILIKE $3 || '%' THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
fi.name
|
||||
@@ -845,7 +844,7 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
)
|
||||
.bind(fid)
|
||||
.bind(&pattern)
|
||||
.bind(&query_lower)
|
||||
.bind(query)
|
||||
.bind(limit_i64)
|
||||
.fetch_all(self.pool.as_ref())
|
||||
.await
|
||||
@@ -861,10 +860,10 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
LEFT JOIN storage.folders fo ON fo.id = fi.folder_id
|
||||
WHERE fi.folder_id IS NULL
|
||||
AND NOT fi.is_trashed
|
||||
AND LOWER(fi.name) LIKE $1
|
||||
AND fi.name ILIKE $1
|
||||
ORDER BY CASE
|
||||
WHEN LOWER(fi.name) = $2 THEN 0
|
||||
WHEN LOWER(fi.name) LIKE $2 || '%' THEN 1
|
||||
WHEN fi.name ILIKE $2 THEN 0
|
||||
WHEN fi.name ILIKE $2 || '%' THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
fi.name
|
||||
@@ -872,7 +871,7 @@ impl FileReadPort for FileBlobReadRepository {
|
||||
"#,
|
||||
)
|
||||
.bind(&pattern)
|
||||
.bind(&query_lower)
|
||||
.bind(query)
|
||||
.bind(limit_i64)
|
||||
.fetch_all(self.pool.as_ref())
|
||||
.await
|
||||
|
||||
@@ -720,15 +720,16 @@ impl FolderRepository for FolderDbRepository {
|
||||
.await;
|
||||
}
|
||||
|
||||
// Build optional name filter
|
||||
// Build optional name filter — use ILIKE (case-insensitive) so the
|
||||
// GIN trigram index idx_folders_name_trgm is used instead of a seq scan.
|
||||
let (name_clause, name_pattern) = match name_contains {
|
||||
Some(name) if !name.is_empty() => (
|
||||
Some(name) if name.len() >= 3 => (
|
||||
if recursive {
|
||||
" AND LOWER(fo.name) LIKE $2"
|
||||
" AND fo.name ILIKE $2"
|
||||
} else {
|
||||
" AND LOWER(fo.name) LIKE $3"
|
||||
" AND fo.name ILIKE $3"
|
||||
},
|
||||
Some(format!("%{}%", name.to_lowercase())),
|
||||
Some(format!("%{}%", name)),
|
||||
),
|
||||
_ => ("", None),
|
||||
};
|
||||
@@ -794,7 +795,7 @@ impl FolderRepository for FolderDbRepository {
|
||||
} else {
|
||||
// Root folders: parent_id IS NULL, reindex params ($1=user_id, $2=pattern)
|
||||
let name_clause_root = match name_contains {
|
||||
Some(name) if !name.is_empty() => " AND LOWER(fo.name) LIKE $2",
|
||||
Some(name) if name.len() >= 3 => " AND fo.name ILIKE $2",
|
||||
_ => "",
|
||||
};
|
||||
format!(
|
||||
@@ -866,9 +867,9 @@ impl FolderRepository for FolderDbRepository {
|
||||
user_id: &str,
|
||||
) -> Result<Vec<Folder>, DomainError> {
|
||||
let (where_extra, name_pattern) = match name_contains {
|
||||
Some(name) if !name.is_empty() => (
|
||||
" AND LOWER(fo.name) LIKE $3",
|
||||
Some(format!("%{}%", name.to_lowercase())),
|
||||
Some(name) if name.len() >= 3 => (
|
||||
" AND fo.name ILIKE $3",
|
||||
Some(format!("%{}%", name)),
|
||||
),
|
||||
_ => ("", None),
|
||||
};
|
||||
@@ -924,8 +925,7 @@ impl FolderRepository for FolderDbRepository {
|
||||
query: &str,
|
||||
limit: usize,
|
||||
) -> Result<Vec<Folder>, DomainError> {
|
||||
let pattern = format!("%{}%", query.to_lowercase());
|
||||
let query_lower = query.to_lowercase();
|
||||
let pattern = format!("%{}%", query);
|
||||
let limit_i64 = limit as i64;
|
||||
|
||||
let rows: Vec<(String, String, String, Option<String>, String, i64, i64)> =
|
||||
@@ -938,10 +938,10 @@ impl FolderRepository for FolderDbRepository {
|
||||
FROM storage.folders
|
||||
WHERE parent_id = $1::uuid
|
||||
AND NOT is_trashed
|
||||
AND LOWER(name) LIKE $2
|
||||
AND name ILIKE $2
|
||||
ORDER BY CASE
|
||||
WHEN LOWER(name) = $3 THEN 0
|
||||
WHEN LOWER(name) LIKE $3 || '%' THEN 1
|
||||
WHEN name ILIKE $3 THEN 0
|
||||
WHEN name ILIKE $3 || '%' THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
name
|
||||
@@ -950,7 +950,7 @@ impl FolderRepository for FolderDbRepository {
|
||||
)
|
||||
.bind(pid)
|
||||
.bind(&pattern)
|
||||
.bind(&query_lower)
|
||||
.bind(query)
|
||||
.bind(limit_i64)
|
||||
.fetch_all(self.pool())
|
||||
.await
|
||||
@@ -963,10 +963,10 @@ impl FolderRepository for FolderDbRepository {
|
||||
FROM storage.folders
|
||||
WHERE parent_id IS NULL
|
||||
AND NOT is_trashed
|
||||
AND LOWER(name) LIKE $1
|
||||
AND name ILIKE $1
|
||||
ORDER BY CASE
|
||||
WHEN LOWER(name) = $2 THEN 0
|
||||
WHEN LOWER(name) LIKE $2 || '%' THEN 1
|
||||
WHEN name ILIKE $2 THEN 0
|
||||
WHEN name ILIKE $2 || '%' THEN 1
|
||||
ELSE 2
|
||||
END,
|
||||
name
|
||||
@@ -974,7 +974,7 @@ impl FolderRepository for FolderDbRepository {
|
||||
"#,
|
||||
)
|
||||
.bind(&pattern)
|
||||
.bind(&query_lower)
|
||||
.bind(query)
|
||||
.bind(limit_i64)
|
||||
.fetch_all(self.pool())
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user