Add search function for files

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent c0011b9e6e
commit b9468b9645

@ -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<i64>,
) -> RepoResult<Vec<Self>> {
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<file::Model>)> = hash::Entity::find()
.find_also_related(file::Entity)
.join(JoinType::Join, hash_tag::Relation::Hash.def())
.filter(condition)
.all(&db)
.await?;
let files: Vec<Self> = 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,

@ -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<String>) -> RepoResult<Vec<File>> {
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<File> {
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<String>) -> RepoResult<Vec<Tag>> {
let tags: Vec<(Option<String>, 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<S: ToString>(&self, tag: S) -> RepoResult<Tag> {
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<Tag> {
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<Tag> {
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

@ -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<S: AsRef<str>>(
/// Returns one tag by name and namespace
pub async fn by_name<S1: ToString>(
db: DatabaseConnection,
name: S,
name: S1,
namespace: Option<String>,
) -> RepoResult<Option<Self>> {
let tag = tag::Entity::find()
.filter(tag::Column::Name.eq(name.as_ref()))
.filter(tag::Column::NamespaceId.eq(Option::<i64>::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<S1: AsRef<str>, S2: AsRef<str>>(
/// Retrieves the namespaced tags by name and namespace
pub async fn all_by_name(
db: DatabaseConnection,
name: S1,
namespace: S2,
) -> RepoResult<Option<Self>> {
let tag = tag::Entity::find()
namespaces_with_names: Vec<(Option<String>, String)>,
) -> RepoResult<Vec<Self>> {
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::<i64>::None))
};
or_condition = or_condition.add(all_condition);
}
let tags: Vec<Self> = 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

@ -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::<FindFilesByTagsRequest>()?;
let repo = get_repo_from_context(ctx).await;
let files = repo.find_files_by_tags(tags.tags).await?;
let responses: Vec<FileResponse> = 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::<AddFileRequest>()?;
@ -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(())

@ -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<String>,
}

Loading…
Cancel
Save