Allow for excluding certain tags from a file search

This commit is contained in:
Cameron
2024-11-23 20:21:19 -05:00
parent 6986540295
commit 9a32a1cfe7
3 changed files with 23 additions and 7 deletions

View File

@@ -113,6 +113,7 @@ pub struct FilesRequest {
pub path: String, pub path: String,
// comma separated numbers // comma separated numbers
pub tag_ids: Option<String>, pub tag_ids: Option<String>,
pub exclude_tag_ids: Option<String>,
pub tag_filter_mode: Option<FilterMode>, pub tag_filter_mode: Option<FilterMode>,
pub recursive: Option<bool>, pub recursive: Option<bool>,
pub sort: Option<SortType>, pub sort: Option<SortType>,

View File

@@ -24,7 +24,7 @@ use crate::tags::TagDao;
use crate::video::StreamActor; use crate::video::StreamActor;
use path_absolutize::*; use path_absolutize::*;
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use rand::{random, thread_rng}; use rand::{thread_rng};
use serde::Deserialize; use serde::Deserialize;
pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>( pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
@@ -39,7 +39,7 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
let search_recursively = req.recursive.unwrap_or(false); let search_recursively = req.recursive.unwrap_or(false);
if let Some(tag_ids) = &req.tag_ids { if let Some(tag_ids) = &req.tag_ids {
if search_recursively { if search_recursively {
let filter_mode = req.tag_filter_mode.unwrap_or(FilterMode::Any); let filter_mode = &req.tag_filter_mode.unwrap_or(FilterMode::Any);
debug!( debug!(
"Searching for tags: {}. With path: '{}' and filter mode: {:?}", "Searching for tags: {}. With path: '{}' and filter mode: {:?}",
tag_ids, search_path, filter_mode tag_ids, search_path, filter_mode
@@ -51,10 +51,16 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
.filter_map(|t| t.parse().ok()) .filter_map(|t| t.parse().ok())
.collect::<Vec<i32>>(); .collect::<Vec<i32>>();
let exclude_tag_ids = req.exclude_tag_ids.clone()
.unwrap_or(String::new())
.split(',')
.filter_map(|t| t.parse().ok())
.collect::<Vec<i32>>();
return dao return dao
.get_files_with_any_tag_ids(tag_ids.clone()) .get_files_with_any_tag_ids(tag_ids.clone(), exclude_tag_ids.clone())
.context(format!("Failed to get files with tag_ids: {:?}", tag_ids)) .context(format!("Failed to get files with tag_ids: {:?}", tag_ids))
.map(|mut tagged_files| { .map(|tagged_files| {
return if let Some(sort_type) = req.sort { return if let Some(sort_type) = req.sort {
debug!("Sorting files: {:?}", sort_type); debug!("Sorting files: {:?}", sort_type);
sort(tagged_files, sort_type) sort(tagged_files, sort_type)

View File

@@ -199,7 +199,11 @@ pub trait TagDao {
fn remove_tag(&mut self, tag_name: &str, path: &str) -> anyhow::Result<Option<()>>; fn remove_tag(&mut self, tag_name: &str, path: &str) -> anyhow::Result<Option<()>>;
fn tag_file(&mut self, path: &str, tag_id: i32) -> anyhow::Result<TaggedPhoto>; fn tag_file(&mut self, path: &str, tag_id: i32) -> anyhow::Result<TaggedPhoto>;
fn get_files_with_all_tag_ids(&mut self, tag_ids: Vec<i32>) -> anyhow::Result<Vec<String>>; fn get_files_with_all_tag_ids(&mut self, tag_ids: Vec<i32>) -> anyhow::Result<Vec<String>>;
fn get_files_with_any_tag_ids(&mut self, tag_ids: Vec<i32>) -> anyhow::Result<Vec<String>>; fn get_files_with_any_tag_ids(
&mut self,
tag_ids: Vec<i32>,
exclude_tag_ids: Vec<i32>,
) -> anyhow::Result<Vec<String>>;
} }
pub struct SqliteTagDao { pub struct SqliteTagDao {
@@ -348,17 +352,22 @@ impl TagDao for SqliteTagDao {
.filter(tagged_photo::tag_id.eq_any(tag_ids.clone())) .filter(tagged_photo::tag_id.eq_any(tag_ids.clone()))
.group_by(tagged_photo::photo_name) .group_by(tagged_photo::photo_name)
.select((tagged_photo::photo_name, count(tagged_photo::tag_id))) .select((tagged_photo::photo_name, count(tagged_photo::tag_id)))
.having(count(tagged_photo::tag_id).eq(tag_ids.len() as i64)) .having(count_distinct(tagged_photo::tag_id).eq(tag_ids.len() as i64))
.select(tagged_photo::photo_name) .select(tagged_photo::photo_name)
.get_results::<String>(&mut self.connection) .get_results::<String>(&mut self.connection)
.with_context(|| format!("Unable to get Tagged photos with ids: {:?}", tag_ids)) .with_context(|| format!("Unable to get Tagged photos with ids: {:?}", tag_ids))
} }
fn get_files_with_any_tag_ids(&mut self, tag_ids: Vec<i32>) -> anyhow::Result<Vec<String>> { fn get_files_with_any_tag_ids(
&mut self,
tag_ids: Vec<i32>,
exclude_tag_ids: Vec<i32>,
) -> anyhow::Result<Vec<String>> {
use diesel::dsl::*; use diesel::dsl::*;
tagged_photo::table tagged_photo::table
.filter(tagged_photo::tag_id.eq_any(tag_ids.clone())) .filter(tagged_photo::tag_id.eq_any(tag_ids.clone()))
.filter(tagged_photo::tag_id.ne_all(exclude_tag_ids))
.group_by(tagged_photo::photo_name) .group_by(tagged_photo::photo_name)
.select((tagged_photo::photo_name, count(tagged_photo::tag_id))) .select((tagged_photo::photo_name, count(tagged_photo::tag_id)))
.select(tagged_photo::photo_name) .select(tagged_photo::photo_name)