Hide folders holding only hidden versions from /photos dirs (#194)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aefc4bc411
commit
0e1fdd43e5
3 files changed
+122
No files matched your search
@@ -228,6 +228,17 @@ pub trait VersionDao: Sync + Send {
|
||||
view_library_ids: &[i32],
|
||||
) -> Result<HashSet<(i32, String)>, DbError>;
|
||||
|
||||
/// Of `dirs` (rel paths), those that hold indexed files and where every
|
||||
/// indexed file anywhere underneath is a version hidden in the view.
|
||||
/// Such a folder would open empty. A folder with no indexed rows at all
|
||||
/// is not reported (nothing says it is safe to hide). One indexed range
|
||||
/// scan per dir; no filesystem walk.
|
||||
fn dirs_holding_only_hidden_versions(
|
||||
&mut self,
|
||||
view_library_ids: &[i32],
|
||||
dirs: &[String],
|
||||
) -> Result<HashSet<String>, DbError>;
|
||||
|
||||
/// Rows in a library whose file name follows the version convention
|
||||
/// (`<name>.<kind>[.<n>].<ext>`), have a hash and aren't linked yet.
|
||||
/// Returns `(id, rel_path, content_hash)` ordered by row id, starting
|
||||
@@ -780,6 +791,71 @@ impl VersionDao for SqliteVersionDao {
|
||||
.map_err(|e| DbError::log(DbErrorKind::QueryError, e))
|
||||
}
|
||||
|
||||
fn dirs_holding_only_hidden_versions(
|
||||
&mut self,
|
||||
view_library_ids: &[i32],
|
||||
dirs: &[String],
|
||||
) -> Result<HashSet<String>, DbError> {
|
||||
if dirs.is_empty() {
|
||||
return Ok(HashSet::new());
|
||||
}
|
||||
trace_db_call("query", "dirs_holding_only_hidden_versions", |_span| {
|
||||
#[derive(QueryableByName)]
|
||||
struct Counts {
|
||||
#[diesel(sql_type = BigInt)]
|
||||
total: i64,
|
||||
#[diesel(sql_type = BigInt)]
|
||||
visible: i64,
|
||||
}
|
||||
#[derive(QueryableByName)]
|
||||
struct Id {
|
||||
#[diesel(sql_type = Integer)]
|
||||
id: i32,
|
||||
}
|
||||
let mut conn = self.connection.lock().expect("Unable to get VersionDao");
|
||||
// Scan the view's libraries (all of them when unscoped) so the
|
||||
// (library_id, rel_path) index serves the prefix range.
|
||||
let scan: Vec<i32> = if view_library_ids.is_empty() {
|
||||
sql_query("SELECT id FROM libraries")
|
||||
.load::<Id>(&mut *conn)?
|
||||
.into_iter()
|
||||
.map(|r| r.id)
|
||||
.collect()
|
||||
} else {
|
||||
view_library_ids.to_vec()
|
||||
};
|
||||
let list = scan
|
||||
.iter()
|
||||
.map(i32::to_string)
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let sql = format!(
|
||||
"SELECT COUNT(*) AS total, \
|
||||
COALESCE(SUM(CASE WHEN {} THEN 1 ELSE 0 END), 0) AS visible \
|
||||
FROM image_exif \
|
||||
WHERE library_id IN ({list}) AND rel_path >= ? AND rel_path < ?",
|
||||
visible_version_clause(view_library_ids)
|
||||
);
|
||||
let mut out = HashSet::new();
|
||||
for dir in dirs {
|
||||
let dir = dir.trim_end_matches('/');
|
||||
if dir.is_empty() {
|
||||
continue;
|
||||
}
|
||||
// '0' is the character after '/', closing the prefix range.
|
||||
let c = sql_query(&sql)
|
||||
.bind::<Text, _>(format!("{dir}/"))
|
||||
.bind::<Text, _>(format!("{dir}0"))
|
||||
.get_result::<Counts>(&mut *conn)?;
|
||||
if c.total > 0 && c.visible == 0 {
|
||||
out.insert(dir.to_string());
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
})
|
||||
.map_err(|e| DbError::log(DbErrorKind::QueryError, e))
|
||||
}
|
||||
|
||||
fn unlinked_name_candidates(
|
||||
&mut self,
|
||||
library_id: i32,
|
||||
|
||||
Reference in new issue
Block a user