From b9468b9645e3c2723442b46da3cc3662864fea6c Mon Sep 17 00:00:00 2001 From: trivernis Date: Fri, 22 Oct 2021 19:51:58 +0200 Subject: [PATCH] Add search function for files Signed-off-by: trivernis --- mediarepo-daemon/mediarepo-model/src/file.rs | 24 ++++++++- mediarepo-daemon/mediarepo-model/src/repo.rs | 19 ++++++- mediarepo-daemon/mediarepo-model/src/tag.rs | 53 +++++++++++-------- .../mediarepo-socket/src/namespaces/files.rs | 26 +++++++-- .../mediarepo-socket/src/types/requests.rs | 5 ++ 5 files changed, 99 insertions(+), 28 deletions(-) diff --git a/mediarepo-daemon/mediarepo-model/src/file.rs b/mediarepo-daemon/mediarepo-model/src/file.rs index 0062d00..d187064 100644 --- a/mediarepo-daemon/mediarepo-model/src/file.rs +++ b/mediarepo-daemon/mediarepo-model/src/file.rs @@ -15,7 +15,7 @@ use mediarepo_database::entities::namespace; use mediarepo_database::entities::tag; use mime::Mime; use sea_orm::prelude::*; -use sea_orm::{DatabaseConnection, Set}; +use sea_orm::{Condition, DatabaseConnection, Set}; use sea_orm::{JoinType, QuerySelect}; use tokio::io::BufReader; @@ -80,6 +80,28 @@ impl File { } } + pub(crate) async fn find_by_tags( + db: DatabaseConnection, + tag_ids: Vec, + ) -> RepoResult> { + let mut condition = Condition::all(); + for tag in tag_ids { + condition = condition.add(hash_tag::Column::TagId.eq(tag)); + } + let results: Vec<(hash::Model, Option)> = hash::Entity::find() + .find_also_related(file::Entity) + .join(JoinType::Join, hash_tag::Relation::Hash.def()) + .filter(condition) + .all(&db) + .await?; + let files: Vec = results + .into_iter() + .filter_map(|(hash, tag)| Some(Self::new(db.clone(), tag?, hash))) + .collect(); + + Ok(files) + } + /// Adds a file with its hash to the database pub(crate) async fn add( db: DatabaseConnection, diff --git a/mediarepo-daemon/mediarepo-model/src/repo.rs b/mediarepo-daemon/mediarepo-model/src/repo.rs index cf50415..67ff58d 100644 --- a/mediarepo-daemon/mediarepo-model/src/repo.rs +++ b/mediarepo-daemon/mediarepo-model/src/repo.rs @@ -82,6 +82,14 @@ impl Repo { File::all(self.db.clone()).await } + /// Finds all files by a list of tags + 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(); + + File::find_by_tags(self.db.clone(), tag_ids).await + } + /// Adds a file to the database by its readable path in the file system pub async fn add_file_by_path(&self, path: PathBuf) -> RepoResult { let mime_match = mime_guess::from_path(&path).first(); @@ -141,6 +149,13 @@ impl Repo { Tag::all(self.db.clone()).await } + /// Finds all tags by name + pub async fn find_all_tags(&self, tags: Vec) -> RepoResult> { + let tags: Vec<(Option, String)> = + tags.into_iter().map(parse_namespace_and_tag).collect(); + Tag::all_by_name(self.db.clone(), tags).await + } + /// Adds or finds a tag pub async fn add_or_find_tag(&self, tag: S) -> RepoResult { let (namespace, name) = parse_namespace_and_tag(tag.to_string()); @@ -153,7 +168,7 @@ impl Repo { /// Adds or finds an unnamespaced tag pub async fn add_or_find_unnamespaced_tag(&self, name: String) -> RepoResult { - if let Some(tag) = Tag::by_name(self.db.clone(), &name).await? { + if let Some(tag) = Tag::by_name(self.db.clone(), &name, None).await? { Ok(tag) } else { self.add_unnamespaced_tag(name).await @@ -171,7 +186,7 @@ impl Repo { name: String, namespace: String, ) -> RepoResult { - if let Some(tag) = Tag::by_name_and_namespace(self.db.clone(), &name, &namespace).await? { + if let Some(tag) = Tag::by_name(self.db.clone(), &name, Some(namespace.clone())).await? { Ok(tag) } else { self.add_namespaced_tag(name, namespace).await diff --git a/mediarepo-daemon/mediarepo-model/src/tag.rs b/mediarepo-daemon/mediarepo-model/src/tag.rs index 9ab7756..4645387 100644 --- a/mediarepo-daemon/mediarepo-model/src/tag.rs +++ b/mediarepo-daemon/mediarepo-model/src/tag.rs @@ -3,7 +3,7 @@ use mediarepo_core::error::RepoResult; use mediarepo_database::entities::namespace; use mediarepo_database::entities::tag; use sea_orm::prelude::*; -use sea_orm::{DatabaseConnection, Set}; +use sea_orm::{Condition, DatabaseConnection, Set}; #[derive(Clone)] pub struct Tag { @@ -50,36 +50,45 @@ impl Tag { Ok(tag) } - /// Retrieves the unnamespaced tag by name - pub async fn by_name>( + /// Returns one tag by name and namespace + pub async fn by_name( db: DatabaseConnection, - name: S, + name: S1, + namespace: Option, ) -> RepoResult> { - let tag = tag::Entity::find() - .filter(tag::Column::Name.eq(name.as_ref())) - .filter(tag::Column::NamespaceId.eq(Option::::None)) - .one(&db) - .await? - .map(|t| Tag::new(db, t, None)); + let mut entries = Self::all_by_name(db, vec![(namespace, name.to_string())]).await?; - Ok(tag) + Ok(entries.pop()) } - /// Retrieves the namespaced tag by name and namespace - pub async fn by_name_and_namespace, S2: AsRef>( + /// Retrieves the namespaced tags by name and namespace + pub async fn all_by_name( db: DatabaseConnection, - name: S1, - namespace: S2, - ) -> RepoResult> { - let tag = tag::Entity::find() + namespaces_with_names: Vec<(Option, String)>, + ) -> RepoResult> { + let mut or_condition = Condition::any(); + + for (namespace, name) in namespaces_with_names { + let mut all_condition = Condition::all().add(tag::Column::Name.eq(name)); + + all_condition = if let Some(namespace) = namespace { + all_condition.add(namespace::Column::Name.eq(namespace)) + } else { + all_condition.add(tag::Column::NamespaceId.eq(Option::::None)) + }; + or_condition = or_condition.add(all_condition); + } + + let tags: Vec = tag::Entity::find() .find_also_related(namespace::Entity) - .filter(namespace::Column::Name.eq(namespace.as_ref())) - .filter(tag::Column::Name.eq(name.as_ref())) - .one(&db) + .filter(or_condition) + .all(&db) .await? - .map(|(t, n)| Self::new(db.clone(), t, n)); + .into_iter() + .map(|(t, n)| Self::new(db.clone(), t, n)) + .collect(); - Ok(tag) + Ok(tags) } /// Adds a new tag to the database diff --git a/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs b/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs index a5f3218..dea6964 100644 --- a/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs +++ b/mediarepo-daemon/mediarepo-socket/src/namespaces/files.rs @@ -1,4 +1,6 @@ -use crate::types::requests::{AddFileRequest, GetFileThumbnailsRequest, ReadFileRequest}; +use crate::types::requests::{ + AddFileRequest, FindFilesByTagsRequest, GetFileThumbnailsRequest, ReadFileRequest, +}; use crate::types::responses::{FileResponse, ThumbnailResponse}; use crate::utils::{file_by_identifier, get_repo_from_context}; use mediarepo_core::error::RepoError; @@ -16,6 +18,7 @@ impl NamespaceProvider for FilesNamespace { fn register(handler: &mut EventHandler) { events!(handler, "all_files" => Self::all_files, + "find_files" => Self::find_files, "add_file" => Self::add_file, "read_file" => Self::read_file, "get_thumbnails" => Self::thumbnails, @@ -38,6 +41,18 @@ impl FilesNamespace { Ok(()) } + /// Searches for files by tags + async fn find_files(ctx: &Context, event: Event) -> IPCResult<()> { + let tags = event.data::()?; + let repo = get_repo_from_context(ctx).await; + let files = repo.find_files_by_tags(tags.tags).await?; + let responses: Vec = files.into_iter().map(FileResponse::from).collect(); + ctx.emitter + .emit_response_to(event.id(), Self::name(), "find_files", responses) + .await?; + Ok(()) + } + /// Adds a file to the repository async fn add_file(ctx: &Context, event: Event) -> IPCResult<()> { let request = event.data::()?; @@ -68,7 +83,7 @@ impl FilesNamespace { reader.read_to_end(&mut buf).await?; ctx.emitter - .emit_response_to(event.id(), Self::name(), "read_file", buf) + .emit_response_to(event.id(), Self::name(), "read_file", BytePayload::new(buf)) .await?; Ok(()) @@ -104,7 +119,12 @@ impl FilesNamespace { let mut buf = Vec::new(); reader.read_to_end(&mut buf).await?; ctx.emitter - .emit_response_to(event.id(), Self::name(), "read_thumbnail", buf) + .emit_response_to( + event.id(), + Self::name(), + "read_thumbnail", + BytePayload::new(buf), + ) .await?; Ok(()) diff --git a/mediarepo-daemon/mediarepo-socket/src/types/requests.rs b/mediarepo-daemon/mediarepo-socket/src/types/requests.rs index 6275703..0e4a7ca 100644 --- a/mediarepo-daemon/mediarepo-socket/src/types/requests.rs +++ b/mediarepo-daemon/mediarepo-socket/src/types/requests.rs @@ -14,3 +14,8 @@ pub enum FileIdentifier { pub type ReadFileRequest = FileIdentifier; pub type GetFileThumbnailsRequest = FileIdentifier; pub type GetFileTagsRequest = FileIdentifier; + +#[derive(Serialize, Deserialize)] +pub struct FindFilesByTagsRequest { + pub tags: Vec, +}