use crate::db::kv::JsonAction;
use crate::db::{get_neo4j_graph, queries, RedisOps};
use crate::models::tag::post::POST_TAGS_KEY_PARTS;
use crate::types::DynError;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use super::PostStream;
#[derive(Serialize, Deserialize, ToSchema, Default, Debug)]
pub struct PostCounts {
pub tags: u32,
pub unique_tags: u32,
pub replies: u32,
pub reposts: u32,
}
impl RedisOps for PostCounts {}
impl PostCounts {
pub async fn get_by_id(author_id: &str, post_id: &str) -> Result<Option<PostCounts>, DynError> {
match Self::get_from_index(author_id, post_id).await? {
Some(counts) => Ok(Some(counts)),
None => {
let graph_response = Self::get_from_graph(author_id, post_id).await?;
if let Some((post_counts, is_reply)) = graph_response {
post_counts
.put_to_index(author_id, post_id, !is_reply)
.await?;
return Ok(Some(post_counts));
}
Ok(None)
}
}
}
pub async fn get_from_index(
author_id: &str,
post_id: &str,
) -> Result<Option<PostCounts>, DynError> {
if let Some(post_counts) = Self::try_from_index_json(&[author_id, post_id], None).await? {
return Ok(Some(post_counts));
}
Ok(None)
}
pub async fn get_from_graph(
author_id: &str,
post_id: &str,
) -> Result<Option<(PostCounts, bool)>, DynError> {
let mut result;
{
let graph = get_neo4j_graph()?;
let query = queries::get::post_counts(author_id, post_id);
let graph = graph.lock().await;
result = graph.execute(query).await?;
}
if let Some(row) = result.next().await? {
let post_exists: bool = row.get("exists").unwrap_or(false);
if post_exists {
let counts: PostCounts = row.get("counts")?;
let is_reply: bool = row.get("is_reply").unwrap_or(false);
return Ok(Some((counts, is_reply)));
}
}
Ok(None)
}
pub async fn put_to_index(
&self,
author_id: &str,
post_id: &str,
is_reply: bool,
) -> Result<(), DynError> {
self.put_index_json(&[author_id, post_id], None, None)
.await?;
if !is_reply {
PostStream::add_to_engagement_sorted_set(self, author_id, post_id).await?;
}
Ok(())
}
pub async fn update_index_field(
index_key: &[&str],
field: &str,
action: JsonAction,
tag_label: Option<&str>,
) -> Result<(), DynError> {
if let Some(label) = tag_label {
let index_parts = [&POST_TAGS_KEY_PARTS[..], index_key].concat();
let score = Self::check_sorted_set_member(None, &index_parts, &[label]).await?;
match (score, &action) {
(Some(tag_value), _) if tag_value < 1 => (),
(None, JsonAction::Increment(_)) => (),
_ => return Ok(()),
}
}
Self::modify_json_field(index_key, field, action).await?;
Ok(())
}
pub async fn reindex(author_id: &str, post_id: &str) -> Result<(), DynError> {
match Self::get_from_graph(author_id, post_id).await? {
Some((counts, is_reply)) => counts.put_to_index(author_id, post_id, is_reply).await?,
None => tracing::error!(
"{}:{} Could not found post counts in the graph",
author_id,
post_id
),
}
Ok(())
}
pub async fn delete(
author_id: &str,
post_id: &str,
remove_from_feeds: bool,
) -> Result<(), DynError> {
Self::remove_from_index_multiple_json(&[&[author_id, post_id]]).await?;
if remove_from_feeds {
PostStream::delete_from_engagement_sorted_set(author_id, post_id).await?;
}
Ok(())
}
}