feat: add media filtering by tag name

This commit is contained in:
2025-12-04 00:12:53 +01:00
parent ad0c04bd20
commit 74d74a128b
3 changed files with 70 additions and 19 deletions

View File

@@ -138,6 +138,7 @@ function MediaPage() {
</SelectTrigger>
<SelectContent>
<SelectItem value="original_filename">Filename</SelectItem>
<SelectItem value="tag.name">Tag</SelectItem>
<SelectItem value="metadata.Make">Make</SelectItem>
<SelectItem value="metadata.Model">Model</SelectItem>
<SelectItem value="metadata.ISO">ISO</SelectItem>

View File

@@ -273,6 +273,55 @@ impl FilterStrategy for MetadataFilterStrategy {
}
}
pub struct TagFilterStrategy;
impl FilterStrategy for TagFilterStrategy {
fn can_handle(&self, field: &str) -> bool {
field == "tag.name"
}
fn apply_join<'a>(
&self,
_query: &mut SqlxQueryBuilder<'a, sqlx::Postgres>,
_field: &'a str,
) -> CoreResult<()> {
// We use EXISTS subqueries in apply_condition, so no main query join is needed.
Ok(())
}
fn apply_condition<'a>(
&self,
query: &mut SqlxQueryBuilder<'a, sqlx::Postgres>,
condition: &'a libertas_core::schema::FilterCondition,
) -> CoreResult<()> {
use libertas_core::schema::FilterOperator;
let op = match condition.operator {
FilterOperator::Eq => "=",
FilterOperator::Neq => "!=",
FilterOperator::Like => "ILIKE",
FilterOperator::Gt => ">",
FilterOperator::Lt => "<",
FilterOperator::Gte => ">=",
FilterOperator::Lte => "<=",
};
query.push(" AND EXISTS (SELECT 1 FROM media_tags mt JOIN tags t ON mt.tag_id = t.id WHERE mt.media_id = media.id AND t.name ");
query.push(op);
query.push(" ");
if condition.operator == FilterOperator::Like {
query.push_bind(format!("%{}%", condition.value));
} else {
query.push_bind(&condition.value);
}
query.push(" ) ");
Ok(())
}
}
pub trait QueryBuilder<T> {
fn apply_options_to_query<'a>(
&self,

View File

@@ -29,13 +29,18 @@ impl PostgresMediaRepository {
allowed_columns.push("date_taken".to_string());
let sort_strategies: Vec<Box<dyn crate::query_builder::SortStrategy>> = vec![
Box::new(crate::query_builder::StandardSortStrategy::new(allowed_columns.clone())),
Box::new(crate::query_builder::StandardSortStrategy::new(
allowed_columns.clone(),
)),
Box::new(crate::query_builder::MetadataSortStrategy),
];
let filter_strategies: Vec<Box<dyn crate::query_builder::FilterStrategy>> = vec![
Box::new(crate::query_builder::StandardFilterStrategy::new(allowed_columns)),
Box::new(crate::query_builder::StandardFilterStrategy::new(
allowed_columns,
)),
Box::new(crate::query_builder::MetadataFilterStrategy),
Box::new(crate::query_builder::TagFilterStrategy),
];
Self {
@@ -125,9 +130,8 @@ impl MediaRepository for PostgresMediaRepository {
count_query.push(" WHERE media.owner_id = ");
count_query.push_bind(user_id);
let (mut count_query, metadata_filter_count) = self
.query_builder
.apply_conditions(count_query, options)?;
let (mut count_query, metadata_filter_count) =
self.query_builder.apply_conditions(count_query, options)?;
if metadata_filter_count > 0 {
count_query.push(" GROUP BY media.id ");
@@ -154,9 +158,8 @@ impl MediaRepository for PostgresMediaRepository {
data_query.push(" WHERE media.owner_id = ");
data_query.push_bind(user_id);
let (mut data_query, metadata_filter_count) = self
.query_builder
.apply_conditions(data_query, options)?;
let (mut data_query, metadata_filter_count) =
self.query_builder.apply_conditions(data_query, options)?;
if metadata_filter_count > 0 {
data_query.push(" GROUP BY media.id ");
@@ -198,9 +201,8 @@ impl MediaRepository for PostgresMediaRepository {
count_query.push(" WHERE fr.person_id = ");
count_query.push_bind(person_id);
let (mut count_query, _metadata_filter_count) = self
.query_builder
.apply_conditions(count_query, options)?;
let (mut count_query, _metadata_filter_count) =
self.query_builder.apply_conditions(count_query, options)?;
let total_items_result = count_query
.build_query_scalar()
@@ -222,9 +224,8 @@ impl MediaRepository for PostgresMediaRepository {
data_query.push(" WHERE fr.person_id = ");
data_query.push_bind(person_id);
let (mut data_query, _metadata_filter_count) = self
.query_builder
.apply_conditions(data_query, options)?;
let (mut data_query, _metadata_filter_count) =
self.query_builder.apply_conditions(data_query, options)?;
data_query.push(" GROUP BY media.id ");