//! Slice-related queries and statistics. //! //! This module handles database operations for slice metadata, including //! collection statistics, actor counts, lexicon counts, and slice discovery. use super::client::Database; use crate::errors::DatabaseError; use crate::models::CollectionStats; impl Database { /// Gets collection statistics for a slice. /// /// Returns record counts and unique actor counts per collection /// (excluding lexicons marked as excludedFromSync). pub async fn get_slice_collection_stats( &self, slice_uri: &str, ) -> Result, DatabaseError> { let stats = sqlx::query!( r#" WITH slice_collections AS ( SELECT DISTINCT json->>'nsid' as collection_nsid FROM record WHERE collection = 'network.slices.lexicon' AND json->>'slice' = $1 AND json->>'nsid' IS NOT NULL AND (json->>'definitions')::jsonb->'main'->>'type' = 'record' AND (json->>'excludedFromSync' IS NULL OR json->>'excludedFromSync' != 'true') ) SELECT r.collection, COUNT(*) as record_count, COUNT(DISTINCT r.did) as unique_actors FROM record r INNER JOIN slice_collections sc ON r.collection = sc.collection_nsid WHERE r.slice_uri = $1 GROUP BY r.collection ORDER BY r.collection "#, slice_uri ) .fetch_all(&self.pool) .await?; Ok(stats .into_iter() .map(|row| CollectionStats { collection: row.collection, record_count: row.record_count.unwrap_or(0), unique_actors: row.unique_actors.unwrap_or(0), }) .collect()) } /// Gets the list of collection NSIDs defined for a slice. /// /// Only includes lexicons with type 'record' that are not excluded from sync. pub async fn get_slice_collections_list( &self, slice_uri: &str, ) -> Result, DatabaseError> { let rows = sqlx::query!( r#" SELECT DISTINCT json->>'nsid' as collection_nsid FROM record WHERE collection = 'network.slices.lexicon' AND json->>'slice' = $1 AND json->>'nsid' IS NOT NULL AND (json->>'definitions')::jsonb->'main'->>'type' = 'record' AND (json->>'excludedFromSync' IS NULL OR json->>'excludedFromSync' != 'true') ORDER BY json->>'nsid' "#, slice_uri ) .fetch_all(&self.pool) .await?; Ok(rows .into_iter() .filter_map(|row| row.collection_nsid) .collect()) } /// Counts total records across all collections in a slice. /// /// Excludes lexicons marked as excludedFromSync. pub async fn get_slice_total_records(&self, slice_uri: &str) -> Result { let count = sqlx::query!( r#" WITH slice_collections AS ( SELECT DISTINCT json->>'nsid' as collection_nsid FROM record WHERE collection = 'network.slices.lexicon' AND json->>'slice' = $1 AND json->>'nsid' IS NOT NULL AND (json->>'definitions')::jsonb->'main'->>'type' = 'record' AND (json->>'excludedFromSync' IS NULL OR json->>'excludedFromSync' != 'true') ) SELECT COUNT(*) as count FROM record r INNER JOIN slice_collections sc ON r.collection = sc.collection_nsid WHERE r.slice_uri = $1 "#, slice_uri ) .fetch_one(&self.pool) .await?; Ok(count.count.unwrap_or(0)) } /// Counts total actors tracked in a slice. pub async fn get_slice_total_actors(&self, slice_uri: &str) -> Result { let count = sqlx::query!( r#" SELECT COUNT(*) as count FROM actor WHERE slice_uri = $1 "#, slice_uri ) .fetch_one(&self.pool) .await?; Ok(count.count.unwrap_or(0)) } /// Counts lexicon definitions for a slice. /// /// Only includes record-type lexicons. pub async fn get_slice_lexicon_count(&self, slice_uri: &str) -> Result { let count = sqlx::query!( r#" SELECT COUNT(*) as count FROM record WHERE collection = 'network.slices.lexicon' AND json->>'slice' = $1 AND (json->>'definitions')::jsonb->'main'->>'type' = 'record' "#, slice_uri ) .fetch_one(&self.pool) .await?; Ok(count.count.unwrap_or(0)) } /// Gets all slice URIs from network.slices.slice records. /// /// Returns all slices that exist in the system pub async fn get_all_slices(&self) -> Result, DatabaseError> { let rows: Vec<(String,)> = sqlx::query_as( r#" SELECT DISTINCT uri as slice_uri FROM record WHERE collection = 'network.slices.slice' "#, ) .fetch_all(&self.pool) .await?; Ok(rows.into_iter().map(|(uri,)| uri).collect()) } /// Gets the domain associated with a slice. /// /// Looks up the network.slices.slice record and extracts the domain field. /// /// # Returns /// Some(domain) if the slice exists and has a domain, None otherwise pub async fn get_slice_domain(&self, slice_uri: &str) -> Result, DatabaseError> { let row = sqlx::query!( r#" SELECT json->>'domain' as domain FROM record WHERE collection = 'network.slices.slice' AND uri = $1 "#, slice_uri ) .fetch_optional(&self.pool) .await?; Ok(row.and_then(|r| r.domain)) } }