refactor: remove all dev.* and public.* schema hardcodes from runtime code
14 files updated to use schema::table_name() instead of hardcoded schema prefixes. Only src/bin/release.rs intentionally retains dev.* references.
This commit is contained in:
@@ -757,10 +757,11 @@ pub async fn run_5w1h_agent(db: &PostgresDb, file_uuid: &str) -> anyhow::Result<
|
||||
let qdrant = QdrantDb::new();
|
||||
qdrant.init_collection(768).await?;
|
||||
|
||||
let chunk_table = schema::table_name("chunk");
|
||||
let rows = sqlx::query_as::<_, (String, String, String, f64, f64)>(
|
||||
r#"SELECT chunk_id, chunk_type, text_content, start_time, end_time
|
||||
FROM dev.chunk WHERE file_uuid = $1 AND chunk_type = 'sentence' AND embedding IS NULL
|
||||
AND (text_content IS NOT NULL AND text_content != '') ORDER BY id"#,
|
||||
&format!("SELECT chunk_id, chunk_type, text_content, start_time, end_time \
|
||||
FROM {} WHERE file_uuid = $1 AND chunk_type = 'sentence' AND embedding IS NULL \
|
||||
AND (text_content IS NOT NULL AND text_content != '') ORDER BY id", chunk_table),
|
||||
)
|
||||
.bind(file_uuid)
|
||||
.fetch_all(db.pool())
|
||||
@@ -776,7 +777,7 @@ pub async fn run_5w1h_agent(db: &PostgresDb, file_uuid: &str) -> anyhow::Result<
|
||||
match embedder.embed_document(text).await {
|
||||
Ok(vector) => {
|
||||
if let Err(e) = sqlx::query(
|
||||
"UPDATE dev.chunk SET embedding = $1::vector WHERE chunk_id = $2 AND file_uuid = $3"
|
||||
&format!("UPDATE {} SET embedding = $1::vector WHERE chunk_id = $2 AND file_uuid = $3", chunk_table)
|
||||
)
|
||||
.bind(&vector as &[f32])
|
||||
.bind(chunk_id)
|
||||
|
||||
@@ -10,6 +10,7 @@ use sqlx::Row;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::api::server::AppState;
|
||||
use crate::core::db::schema;
|
||||
use crate::core::db::PostgresDb;
|
||||
|
||||
pub fn identity_agent_routes() -> Router<AppState> {
|
||||
@@ -204,7 +205,7 @@ async fn analyze_identity(
|
||||
});
|
||||
|
||||
let _ = sqlx::query(
|
||||
"INSERT INTO dev.identities (name, identity_type, source, metadata, status) VALUES ($1, 'people', 'auto', $2::jsonb, 'pending') ON CONFLICT DO NOTHING"
|
||||
&format!("INSERT INTO {} (name, identity_type, source, metadata, status) VALUES ($1, 'people', 'auto', $2::jsonb, 'pending') ON CONFLICT DO NOTHING", schema::table_name("identities"))
|
||||
)
|
||||
.bind(&identity_name)
|
||||
.bind(&metadata)
|
||||
@@ -473,21 +474,21 @@ async fn suggest_clustering(
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
let fd_table = schema::table_name("face_detections");
|
||||
let identities_table = schema::table_name("identities");
|
||||
let query = format!(
|
||||
r#"
|
||||
SELECT trace_id, file_uuid, COUNT(*) as face_count
|
||||
FROM dev.face_detections fd
|
||||
WHERE fd.trace_id IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM dev.identities i
|
||||
WHERE i.metadata->>'trace_id' = fd.trace_id::text
|
||||
)
|
||||
{}
|
||||
GROUP BY trace_id, file_uuid
|
||||
HAVING COUNT(*) >= $1
|
||||
ORDER BY face_count DESC
|
||||
"#,
|
||||
file_filter
|
||||
"SELECT trace_id, file_uuid, COUNT(*) as face_count \
|
||||
FROM {} fd \
|
||||
WHERE fd.trace_id IS NOT NULL \
|
||||
AND NOT EXISTS ( \
|
||||
SELECT 1 FROM {} i \
|
||||
WHERE i.metadata->>'trace_id' = fd.trace_id::text \
|
||||
) \
|
||||
{} \
|
||||
GROUP BY trace_id, file_uuid \
|
||||
HAVING COUNT(*) >= $1 \
|
||||
ORDER BY face_count DESC",
|
||||
fd_table, identities_table, file_filter
|
||||
);
|
||||
|
||||
let pool = state.db.pool();
|
||||
@@ -660,8 +661,9 @@ fn cosine_similarity(a: &[f32], b: &[f32]) -> f32 {
|
||||
/// Round 2+: 用已匹配 trace 的所有 face 作為 seed,傳播到未匹配 trace
|
||||
async fn match_faces_iterative(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::Result<usize> {
|
||||
// Step 1: 載入 TMDb identities (source='tmdb' 且有 face_embedding)
|
||||
let identities_table = schema::table_name("identities");
|
||||
let tmdb_rows = sqlx::query_as::<_, (i32, String, Vec<f32>)>(
|
||||
"SELECT id, name, face_embedding::real[] FROM dev.identities WHERE source='tmdb' AND face_embedding IS NOT NULL"
|
||||
&format!("SELECT id, name, face_embedding::real[] FROM {} WHERE source='tmdb' AND face_embedding IS NOT NULL", identities_table)
|
||||
)
|
||||
.fetch_all(pool).await?;
|
||||
|
||||
@@ -675,10 +677,11 @@ async fn match_faces_iterative(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::
|
||||
);
|
||||
|
||||
// Step 2: 載入所有 face_detections,按 trace_id 分組
|
||||
let fd_table = schema::table_name("face_detections");
|
||||
let fd_rows = sqlx::query_as::<_, (i32, Vec<f32>)>(
|
||||
"SELECT trace_id, embedding FROM dev.face_detections \
|
||||
&format!("SELECT trace_id, embedding FROM {} \
|
||||
WHERE file_uuid=$1 AND trace_id IS NOT NULL AND embedding IS NOT NULL \
|
||||
ORDER BY trace_id",
|
||||
ORDER BY trace_id", fd_table),
|
||||
)
|
||||
.bind(file_uuid)
|
||||
.fetch_all(pool)
|
||||
@@ -797,17 +800,19 @@ async fn match_faces_iterative(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::
|
||||
}
|
||||
|
||||
// Step 5: 寫入 DB
|
||||
let identities_table = schema::table_name("identities");
|
||||
let fd_table = schema::table_name("face_detections");
|
||||
let mut updated = 0usize;
|
||||
for (tid, name) in &matched {
|
||||
let id_opt = sqlx::query_scalar::<_, Option<i32>>(
|
||||
"SELECT id FROM dev.identities WHERE name=$1 AND source='tmdb'",
|
||||
&format!("SELECT id FROM {} WHERE name=$1 AND source='tmdb'", identities_table),
|
||||
)
|
||||
.bind(name)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
if let Some(identity_id) = id_opt {
|
||||
let _ = sqlx::query(
|
||||
"UPDATE dev.face_detections SET identity_id=$1 WHERE file_uuid=$2 AND trace_id=$3",
|
||||
&format!("UPDATE {} SET identity_id=$1 WHERE file_uuid=$2 AND trace_id=$3", fd_table),
|
||||
)
|
||||
.bind(identity_id)
|
||||
.bind(file_uuid)
|
||||
@@ -833,10 +838,11 @@ async fn match_faces_iterative(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::
|
||||
/// and stores bindings in identity_bindings table.
|
||||
pub async fn bind_speakers(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::Result<usize> {
|
||||
// Load face traces with identity_id and frame numbers
|
||||
let fd_table = schema::table_name("face_detections");
|
||||
let traces = sqlx::query_as::<_, (i32, Vec<i32>)>(
|
||||
"SELECT trace_id, array_agg(frame_number ORDER BY frame_number) \
|
||||
FROM dev.face_detections WHERE file_uuid=$1 AND trace_id IS NOT NULL AND identity_id IS NOT NULL \
|
||||
GROUP BY trace_id"
|
||||
&format!("SELECT trace_id, array_agg(frame_number ORDER BY frame_number) \
|
||||
FROM {} WHERE file_uuid=$1 AND trace_id IS NOT NULL AND identity_id IS NOT NULL \
|
||||
GROUP BY trace_id", fd_table)
|
||||
)
|
||||
.bind(file_uuid)
|
||||
.fetch_all(pool).await?;
|
||||
@@ -903,8 +909,9 @@ pub async fn bind_speakers(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::Resu
|
||||
}
|
||||
|
||||
// Get identity_id for this trace
|
||||
let fd_table = schema::table_name("face_detections");
|
||||
let identity_id: Option<i32> = sqlx::query_scalar(
|
||||
"SELECT identity_id FROM dev.face_detections WHERE file_uuid=$1 AND trace_id=$2 AND identity_id IS NOT NULL LIMIT 1"
|
||||
&format!("SELECT identity_id FROM {} WHERE file_uuid=$1 AND trace_id=$2 AND identity_id IS NOT NULL LIMIT 1", fd_table)
|
||||
)
|
||||
.bind(file_uuid).bind(trace_id)
|
||||
.fetch_optional(pool).await?.flatten();
|
||||
@@ -945,10 +952,11 @@ pub async fn bind_speakers(pool: &sqlx::PgPool, file_uuid: &str) -> anyhow::Resu
|
||||
"overlap_ratio": overlap_ratio,
|
||||
});
|
||||
|
||||
let ib_table = schema::table_name("identity_bindings");
|
||||
let _ = sqlx::query(
|
||||
"INSERT INTO dev.identity_bindings (identity_id, identity_type, identity_value, confidence, metadata) \
|
||||
&format!("INSERT INTO {} (identity_id, identity_type, identity_value, confidence, metadata) \
|
||||
VALUES ($1, 'speaker', $2, $3, $4::jsonb) \
|
||||
ON CONFLICT (identity_id, identity_type, identity_value) DO UPDATE SET confidence = EXCLUDED.confidence, metadata = EXCLUDED.metadata"
|
||||
ON CONFLICT (identity_id, identity_type, identity_value) DO UPDATE SET confidence = EXCLUDED.confidence, metadata = EXCLUDED.metadata", ib_table)
|
||||
)
|
||||
.bind(identity_id)
|
||||
.bind(&best_speaker)
|
||||
@@ -1025,7 +1033,7 @@ pub async fn run_identity_agent(db: &PostgresDb, file_uuid: &str) -> anyhow::Res
|
||||
"reasoning": id_result.reasoning,
|
||||
});
|
||||
let _ = sqlx::query(
|
||||
"INSERT INTO dev.identities (name, identity_type, source, metadata, status) VALUES ($1, 'people', 'auto', $2::jsonb, 'pending') ON CONFLICT DO NOTHING"
|
||||
&format!("INSERT INTO {} (name, identity_type, source, metadata, status) VALUES ($1, 'people', 'auto', $2::jsonb, 'pending') ON CONFLICT DO NOTHING", schema::table_name("identities"))
|
||||
)
|
||||
.bind(&identity_name)
|
||||
.bind(&metadata)
|
||||
|
||||
@@ -324,9 +324,10 @@ async fn trace_video(
|
||||
let pad_frames = (padding * fps) as i32;
|
||||
|
||||
// Query identity info for this trace
|
||||
let identities_table = schema::table_name("identities");
|
||||
let identity_name: String = sqlx::query_scalar(&format!(
|
||||
"SELECT COALESCE(i.name, 'unknown') FROM {} fd LEFT JOIN dev.identities i ON i.id = fd.identity_id WHERE fd.file_uuid = $1 AND fd.trace_id = $2 LIMIT 1",
|
||||
face_table
|
||||
"SELECT COALESCE(i.name, 'unknown') FROM {} fd LEFT JOIN {} i ON i.id = fd.identity_id WHERE fd.file_uuid = $1 AND fd.trace_id = $2 LIMIT 1",
|
||||
face_table, identities_table
|
||||
))
|
||||
.bind(&file_uuid).bind(trace_id)
|
||||
.fetch_optional(state.db.pool()).await
|
||||
@@ -334,8 +335,9 @@ async fn trace_video(
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
|
||||
// Query cut_id for the first frame
|
||||
let cut_table = schema::table_name("cut");
|
||||
let cut_id: i32 = sqlx::query_scalar(
|
||||
"SELECT scene_number FROM public.cut WHERE file_uuid = $1 AND start_frame <= $2 AND end_frame >= $2 LIMIT 1"
|
||||
&format!("SELECT scene_number FROM {} WHERE file_uuid = $1 AND start_frame <= $2 AND end_frame >= $2 LIMIT 1", cut_table)
|
||||
)
|
||||
.bind(&file_uuid).bind(first_frame)
|
||||
.fetch_optional(state.db.pool()).await
|
||||
|
||||
@@ -658,8 +658,9 @@ async fn register_single_file(
|
||||
}
|
||||
};
|
||||
|
||||
let videos_table = schema::table_name("videos");
|
||||
let birthday = sqlx::query_scalar::<_, chrono::DateTime<chrono::Utc>>(
|
||||
"SELECT registration_time FROM dev.videos WHERE file_name = $1 AND registration_time IS NOT NULL LIMIT 1"
|
||||
&format!("SELECT registration_time FROM {} WHERE file_name = $1 AND registration_time IS NOT NULL LIMIT 1", videos_table)
|
||||
)
|
||||
.bind(&file_name)
|
||||
.fetch_optional(db.pool())
|
||||
@@ -900,8 +901,9 @@ async fn register_single_file(
|
||||
}
|
||||
}
|
||||
}
|
||||
let videos_table = schema::table_name("videos");
|
||||
let _ = sqlx::query(
|
||||
"UPDATE dev.videos SET cut_done = $1, scene_done = $2, audio_tracks = $3, cut_count = $4, cut_max_duration = $5 WHERE file_uuid = $6"
|
||||
&format!("UPDATE {} SET cut_done = $1, scene_done = $2, audio_tracks = $3, cut_count = $4, cut_max_duration = $5 WHERE file_uuid = $6", videos_table)
|
||||
)
|
||||
.bind(cut_done).bind(scene_done).bind(&audio_tracks_json).bind(cut_count).bind(cut_max_duration).bind(&file_uuid)
|
||||
.execute(db.pool()).await;
|
||||
|
||||
@@ -10,7 +10,7 @@ use axum::{
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::core::db::{Database, PostgresDb};
|
||||
use crate::core::db::{schema, Database, PostgresDb};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UniversalSearchRequest {
|
||||
@@ -326,9 +326,10 @@ async fn search_chunks(
|
||||
None => return Err(anyhow::anyhow!("uuid is required for chunk search")),
|
||||
};
|
||||
|
||||
let chunk_table = schema::table_name("chunk");
|
||||
let mut sql = format!(
|
||||
"SELECT chunk_id, chunk_type, start_time, end_time, start_frame, end_frame, text_content, content FROM dev.chunk WHERE file_uuid = '{}'",
|
||||
uuid
|
||||
"SELECT chunk_id, chunk_type, start_time, end_time, start_frame, end_frame, text_content, content FROM {} WHERE file_uuid = '{}'",
|
||||
chunk_table, uuid
|
||||
);
|
||||
if let Some(tr) = &req.time_range {
|
||||
sql.push_str(&format!(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! - Object relationships
|
||||
|
||||
use crate::core::chunk::types::{Chunk, ChunkRule, ChunkType};
|
||||
use crate::core::db::PostgresDb;
|
||||
use crate::core::db::{schema, PostgresDb};
|
||||
use anyhow::Result;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
@@ -176,9 +176,10 @@ pub async fn search_visual_chunks(
|
||||
|
||||
/// Get all visual chunks for a video UUID
|
||||
async fn get_visual_chunks_by_uuid(db: &PostgresDb, uuid: &str) -> Result<Vec<Chunk>> {
|
||||
let chunk_table = schema::table_name("chunk");
|
||||
let sql = format!(
|
||||
"SELECT file_id, file_uuid, chunk_id, chunk_type, fps, start_frame, end_frame, text_content, content, metadata, vector_id, visual_stats FROM dev.chunk WHERE file_uuid = '{}' AND chunk_type = 'visual' ORDER BY start_frame ASC",
|
||||
uuid.replace('\'', "''")
|
||||
"SELECT file_id, file_uuid, chunk_id, chunk_type, fps, start_frame, end_frame, text_content, content, metadata, vector_id, visual_stats FROM {} WHERE file_uuid = '{}' AND chunk_type = 'visual' ORDER BY start_frame ASC",
|
||||
chunk_table, uuid.replace('\'', "''")
|
||||
);
|
||||
|
||||
let rows: Vec<(
|
||||
@@ -373,6 +374,7 @@ pub async fn get_visual_chunk_statistics(
|
||||
db: &PostgresDb,
|
||||
uuid: &str,
|
||||
) -> Result<HashMap<String, Value>> {
|
||||
let chunk_table = schema::table_name("chunk");
|
||||
let sql = format!(
|
||||
"SELECT
|
||||
COUNT(*) as total_chunks,
|
||||
@@ -381,9 +383,10 @@ pub async fn get_visual_chunk_statistics(
|
||||
MAX((content->'metadata'->>'avg_confidence')::float) as max_confidence,
|
||||
SUM((content->'metadata'->>'object_count')::int) as total_objects,
|
||||
AVG((content->'metadata'->>'spatial_density')::float) as avg_density
|
||||
FROM dev.chunk
|
||||
FROM {}
|
||||
WHERE file_uuid = '{}'
|
||||
AND chunk_type = 'visual'",
|
||||
chunk_table,
|
||||
uuid.replace('\'', "''")
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user