From b9bf5861b684a9c71199736fba91d51fc585c923 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 26 Oct 2021 22:16:29 +0200 Subject: [PATCH] Add support for negated tag queries Signed-off-by: trivernis --- mediarepo-daemon/Cargo.lock | 2 +- mediarepo-daemon/mediarepo-model/src/file.rs | 34 +++++++++++++------ mediarepo-daemon/mediarepo-model/src/repo.rs | 22 ++++++++++-- mediarepo-daemon/mediarepo-model/src/tag.rs | 11 +++++- mediarepo-daemon/mediarepo-socket/Cargo.lock | 2 +- mediarepo-daemon/mediarepo-socket/Cargo.toml | 2 +- .../mediarepo-socket/src/namespaces/files.rs | 2 +- 7 files changed, 56 insertions(+), 19 deletions(-) diff --git a/mediarepo-daemon/Cargo.lock b/mediarepo-daemon/Cargo.lock index 563e0f3..dec02f0 100644 --- a/mediarepo-daemon/Cargo.lock +++ b/mediarepo-daemon/Cargo.lock @@ -841,7 +841,7 @@ dependencies = [ [[package]] name = "mediarepo-api" version = "0.1.0" -source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=af90986e88cc4ef7d797ecc9cfd0c306b2d4c7cd#af90986e88cc4ef7d797ecc9cfd0c306b2d4c7cd" +source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=6cb2b0e467b3554b07d04f98d9244d8e4952db68#6cb2b0e467b3554b07d04f98d9244d8e4952db68" dependencies = [ "chrono", "serde", diff --git a/mediarepo-daemon/mediarepo-model/src/file.rs b/mediarepo-daemon/mediarepo-model/src/file.rs index abed53b..398ad0f 100644 --- a/mediarepo-daemon/mediarepo-model/src/file.rs +++ b/mediarepo-daemon/mediarepo-model/src/file.rs @@ -90,20 +90,32 @@ impl File { #[tracing::instrument(level = "debug", skip(db))] pub(crate) async fn find_by_tags( db: DatabaseConnection, - tag_ids: Vec, + tag_ids: Vec<(i64, bool)>, ) -> RepoResult> { let mut condition = Condition::all(); - for tag in tag_ids { - condition = condition.add( - hash::Column::Id.in_subquery( - Query::select() - .expr(Expr::col(hash_tag::Column::HashId)) - .from(hash_tag::Entity) - .cond_where(hash_tag::Column::TagId.eq(tag)) - .to_owned(), - ), - ); + for (tag, negated) in tag_ids { + condition = if negated { + condition.add( + hash::Column::Id.not_in_subquery( + Query::select() + .expr(Expr::col(hash_tag::Column::HashId)) + .from(hash_tag::Entity) + .cond_where(hash_tag::Column::TagId.eq(tag)) + .to_owned(), + ), + ) + } else { + condition.add( + hash::Column::Id.in_subquery( + Query::select() + .expr(Expr::col(hash_tag::Column::HashId)) + .from(hash_tag::Entity) + .cond_where(hash_tag::Column::TagId.eq(tag)) + .to_owned(), + ), + ) + } } let results: Vec<(hash::Model, Option)> = hash::Entity::find() .find_also_related(file::Entity) diff --git a/mediarepo-daemon/mediarepo-model/src/repo.rs b/mediarepo-daemon/mediarepo-model/src/repo.rs index 782b6a9..1fe56cf 100644 --- a/mediarepo-daemon/mediarepo-model/src/repo.rs +++ b/mediarepo-daemon/mediarepo-model/src/repo.rs @@ -9,8 +9,10 @@ use mediarepo_core::image_processing::ThumbnailSize; use mediarepo_core::utils::parse_namespace_and_tag; use mediarepo_database::get_database; use sea_orm::DatabaseConnection; +use std::collections::HashMap; use std::fmt::Debug; use std::io::Cursor; +use std::iter::FromIterator; use std::path::PathBuf; use tokio::fs::OpenOptions; use tokio::io::BufReader; @@ -97,9 +99,23 @@ impl Repo { /// Finds all files by a list of tags #[tracing::instrument(level = "debug", skip(self))] - pub async fn find_files_by_tags(&self, tags: Vec) -> RepoResult> { - let tags = self.find_all_tags(tags).await?; - let tag_ids = tags.into_iter().map(|tag| tag.id()).collect(); + pub async fn find_files_by_tags(&self, tags: Vec<(String, bool)>) -> RepoResult> { + let db_tags = self + .find_all_tags(tags.iter().map(|t| t.0.clone()).collect()) + .await?; + let tag_map: HashMap = HashMap::from_iter(tags.into_iter()); + let tag_ids: Vec<(i64, bool)> = db_tags + .into_iter() + .map(|tag| { + ( + tag.id(), + tag_map + .get(&tag.normalized_name()) + .cloned() + .unwrap_or(false), + ) + }) + .collect(); File::find_by_tags(self.db.clone(), tag_ids).await } diff --git a/mediarepo-daemon/mediarepo-model/src/tag.rs b/mediarepo-daemon/mediarepo-model/src/tag.rs index 5c7f8bc..0576245 100644 --- a/mediarepo-daemon/mediarepo-model/src/tag.rs +++ b/mediarepo-daemon/mediarepo-model/src/tag.rs @@ -32,7 +32,7 @@ impl Tag { #[tracing::instrument(level = "debug", skip(db))] pub async fn all(db: DatabaseConnection) -> RepoResult> { let tags: Vec = tag::Entity::find() - .inner_join(namespace::Entity) + .left_join(namespace::Entity) .select_also(namespace::Entity) .all(&db) .await? @@ -135,4 +135,13 @@ impl Tag { .clone() .map(|n| Namespace::new(self.db.clone(), n)) } + + /// Returns the normalized name of the tag (namespace:tag) + pub fn normalized_name(&self) -> String { + if let Some(namespace) = &self.namespace { + format!("{}:{}", namespace.name, self.model.name) + } else { + self.model.name.to_owned() + } + } } diff --git a/mediarepo-daemon/mediarepo-socket/Cargo.lock b/mediarepo-daemon/mediarepo-socket/Cargo.lock index 7f4acd6..8616341 100644 --- a/mediarepo-daemon/mediarepo-socket/Cargo.lock +++ b/mediarepo-daemon/mediarepo-socket/Cargo.lock @@ -764,7 +764,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" version = "0.1.0" -source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=af90986e88cc4ef7d797ecc9cfd0c306b2d4c7cd#af90986e88cc4ef7d797ecc9cfd0c306b2d4c7cd" +source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=6cb2b0e467b3554b07d04f98d9244d8e4952db68#6cb2b0e467b3554b07d04f98d9244d8e4952db68" dependencies = [ "chrono", "serde", diff --git a/mediarepo-daemon/mediarepo-socket/Cargo.toml b/mediarepo-daemon/mediarepo-socket/Cargo.toml index d1f1cb5..9faa014 100644 --- a/mediarepo-daemon/mediarepo-socket/Cargo.toml +++ b/mediarepo-daemon/mediarepo-socket/Cargo.toml @@ -29,4 +29,4 @@ features = ["tokio-executor"] [dependencies.mediarepo-api] git = "https://github.com/Trivernis/mediarepo-api.git" -rev = "af90986e88cc4ef7d797ecc9cfd0c306b2d4c7cd" \ No newline at end of file +rev = "6cb2b0e467b3554b07d04f98d9244d8e4952db68" \ No newline at end of file diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs index 2b0cd44..c36dea2 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs @@ -51,7 +51,7 @@ impl FilesNamespace { async fn find_files(ctx: &Context, event: Event) -> IPCResult<()> { let tags = event.data::()?; let repo = get_repo_from_context(ctx).await; - let tags = tags.tags.into_iter().map(|t| t.name).collect(); + let tags = tags.tags.into_iter().map(|t| (t.name, t.negate)).collect(); let files = repo.find_files_by_tags(tags).await?; let responses: Vec = files .into_iter()