diff options
Diffstat (limited to 'src/database')
-rw-r--r-- | src/database/file/mod.rs | 14 | ||||
-rw-r--r-- | src/database/memory.rs | 4 | ||||
-rw-r--r-- | src/database/mod.rs | 5 | ||||
-rw-r--r-- | src/database/postgres/mod.rs | 15 |
4 files changed, 37 insertions, 1 deletions
diff --git a/src/database/file/mod.rs b/src/database/file/mod.rs index 27d3da1..46660ab 100644 --- a/src/database/file/mod.rs +++ b/src/database/file/mod.rs @@ -254,6 +254,20 @@ async fn hydrate_author<S: Storage>( #[async_trait] impl Storage for FileStorage { #[tracing::instrument(skip(self))] + async fn categories(&self, url: &str) -> Result<Vec<String>> { + // This requires an expensive scan through the entire + // directory tree. + // + // Until this backend has some kind of caching/indexing for + // categories (consider using symlinks?), this query won't + // perform well. + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "?q=category queries are not implemented due to resource constraints" + ))? + } + + #[tracing::instrument(skip(self))] async fn post_exists(&self, url: &str) -> Result<bool> { let path = url_to_path(&self.root_dir, url); debug!("Checking if {:?} exists...", path); diff --git a/src/database/memory.rs b/src/database/memory.rs index 6339e7a..564f451 100644 --- a/src/database/memory.rs +++ b/src/database/memory.rs @@ -16,6 +16,10 @@ pub struct MemoryStorage { #[async_trait] impl Storage for MemoryStorage { + async fn categories(&self, _url: &str) -> Result<Vec<String>> { + unimplemented!() + } + async fn post_exists(&self, url: &str) -> Result<bool> { return Ok(self.mapping.read().await.contains_key(url)); } diff --git a/src/database/mod.rs b/src/database/mod.rs index b4b70b2..a6a3b46 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -215,6 +215,9 @@ pub type Result<T> = std::result::Result<T, StorageError>; /// or lock the database so that write conflicts or reading half-written data should not occur. #[async_trait] pub trait Storage: std::fmt::Debug + Clone + Send + Sync { + /// Return the list of categories used in blog posts of a specified blog. + async fn categories(&self, url: &str) -> Result<Vec<String>>; + /// Check if a post exists in the database. async fn post_exists(&self, url: &str) -> Result<bool>; @@ -236,7 +239,7 @@ pub trait Storage: std::fmt::Debug + Clone + Send + Sync { } /// Remove post from feed. Some database implementations might have optimized ways to do this. #[tracing::instrument(skip(self))] - async fn remove_from_feed(&self, feed: &'_ str, post: &'_ str) -> Result<()> { + async fn remove_from_feed(&self, feed: &str, post: &str) -> Result<()> { tracing::debug!("Removing {} into {} using `update_post`", post, feed); self.update_post(feed, serde_json::from_value( serde_json::json!({"delete": {"children": [post]}})).unwrap() diff --git a/src/database/postgres/mod.rs b/src/database/postgres/mod.rs index ee7dd1c..71c4d58 100644 --- a/src/database/postgres/mod.rs +++ b/src/database/postgres/mod.rs @@ -75,6 +75,21 @@ impl PostgresStorage { #[async_trait::async_trait] impl Storage for PostgresStorage { #[tracing::instrument(skip(self))] + async fn categories(&self, url: &str) -> Result<Vec<String>> { + sqlx::query_scalar::<_, String>(" +SELECT jsonb_array_elements(mf2['properties']['category']) AS category +FROM kittybox.mf2_json +WHERE + jsonb_typeof(mf2['properties']['category']) = 'array' + AND uid LIKE ($1 + '%') + GROUP BY category ORDER BY count(*) DESC +") + .bind(url) + .fetch_all(&self.db) + .await + .map_err(|err| err.into()) + } + #[tracing::instrument(skip(self))] async fn post_exists(&self, url: &str) -> Result<bool> { sqlx::query_as::<_, (bool,)>("SELECT exists(SELECT 1 FROM kittybox.mf2_json WHERE uid = $1 OR mf2['properties']['url'] ? $1)") .bind(url) |