feat: category filtering knowledge and content

This commit is contained in:
Per Stark
2025-04-15 11:20:55 +02:00
parent 77b157b3b8
commit fa619a66ba
13 changed files with 492 additions and 121 deletions

View File

@@ -11,7 +11,7 @@ use tokio::fs::remove_dir_all;
use tracing::info;
use uuid::Uuid;
use crate::{storage::db::SurrealDbClient, stored_object};
use crate::{error::AppError, storage::db::SurrealDbClient, stored_object};
#[derive(Error, Debug)]
pub enum FileError {
@@ -221,15 +221,15 @@ impl FileInfo {
///
/// # Returns
/// `Result<(), FileError>`
pub async fn delete_by_id(id: &str, db_client: &SurrealDbClient) -> Result<(), FileError> {
pub async fn delete_by_id(id: &str, db_client: &SurrealDbClient) -> Result<(), AppError> {
// Get the FileInfo from the database
let file_info = match db_client.get_item::<FileInfo>(id).await? {
Some(info) => info,
None => {
return Err(FileError::FileNotFound(format!(
return Err(AppError::from(FileError::FileNotFound(format!(
"File with id {} was not found",
id
)))
))))
}
};
@@ -242,15 +242,15 @@ impl FileInfo {
remove_dir_all(parent_dir).await?;
info!("Removed directory {:?} and its contents", parent_dir);
} else {
return Err(FileError::FileNotFound(
return Err(AppError::from(FileError::FileNotFound(
"File has no parent directory".to_string(),
));
)));
}
} else {
return Err(FileError::FileNotFound(format!(
return Err(AppError::from(FileError::FileNotFound(format!(
"File at path {:?} was not found",
file_path
)));
))));
}
// Delete the FileInfo from the database

View File

@@ -1,5 +1,4 @@
use crate::{error::AppError, storage::types::file_info::FileInfo};
use chrono::Utc;
use serde::{Deserialize, Serialize};
use tracing::info;
use url::Url;

View File

@@ -227,6 +227,50 @@ impl User {
Ok(entities)
}
pub async fn get_knowledge_entities_by_type(
user_id: &str,
entity_type: &str,
db: &SurrealDbClient,
) -> Result<Vec<KnowledgeEntity>, AppError> {
let entities: Vec<KnowledgeEntity> = db
.client
.query("SELECT * FROM type::table($table) WHERE user_id = $user_id AND entity_type = $entity_type")
.bind(("table", KnowledgeEntity::table_name()))
.bind(("user_id", user_id.to_owned()))
.bind(("entity_type", entity_type.to_owned()))
.await?
.take(0)?;
Ok(entities)
}
pub async fn get_entity_types(
user_id: &str,
db: &SurrealDbClient,
) -> Result<Vec<String>, AppError> {
#[derive(Deserialize)]
struct EntityTypeResponse {
entity_type: String,
}
// Query to select distinct entity types for the user
let response: Vec<EntityTypeResponse> = db
.client
.query("SELECT entity_type FROM type::table($table_name) WHERE user_id = $user_id GROUP BY entity_type")
.bind(("user_id", user_id.to_owned()))
.bind(("table_name", KnowledgeEntity::table_name()))
.await?
.take(0)?;
// Extract the entity types from the response
let entity_types: Vec<String> = response
.into_iter()
.map(|item| format!("{:?}", item.entity_type))
.collect();
Ok(entity_types)
}
pub async fn get_knowledge_relationships(
user_id: &str,
db: &SurrealDbClient,
@@ -272,6 +316,23 @@ impl User {
Ok(items)
}
pub async fn get_text_contents_by_category(
user_id: &str,
category: &str,
db: &SurrealDbClient,
) -> Result<Vec<TextContent>, AppError> {
let items: Vec<TextContent> = db
.client
.query("SELECT * FROM type::table($table_name) WHERE user_id = $user_id AND category = $category ORDER BY created_at DESC")
.bind(("user_id", user_id.to_owned()))
.bind(("category", category.to_owned()))
.bind(("table_name", TextContent::table_name()))
.await?
.take(0)?;
Ok(items)
}
pub async fn get_latest_knowledge_entities(
user_id: &str,
db: &SurrealDbClient,
@@ -413,6 +474,34 @@ impl User {
Ok(())
}
pub async fn get_knowledge_entities_by_content_category(
user_id: &str,
category: &str,
db: &SurrealDbClient,
) -> Result<Vec<KnowledgeEntity>, AppError> {
// First, find all text content items with the specified category
let text_contents = Self::get_text_contents_by_category(user_id, category, db).await?;
if text_contents.is_empty() {
return Ok(Vec::new());
}
// Extract source_ids
let source_ids: Vec<String> = text_contents.iter().map(|tc| tc.id.clone()).collect();
// Find all knowledge entities with matching source_ids
let entities: Vec<KnowledgeEntity> = db
.client
.query("SELECT * FROM type::table($table) WHERE user_id = $user_id AND source_id IN $source_ids")
.bind(("table", KnowledgeEntity::table_name()))
.bind(("user_id", user_id.to_owned()))
.bind(("source_ids", source_ids))
.await?
.take(0)?;
Ok(entities)
}
}
#[cfg(test)]