Skip to content

Commit

Permalink
Revert "Refactor TagPost (#96)"
Browse files Browse the repository at this point in the history
This reverts commit 63090e5.
  • Loading branch information
tipogi authored Aug 30, 2024
1 parent 63090e5 commit 8f3555b
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 24 deletions.
2 changes: 1 addition & 1 deletion benches/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn bench_get_post_tags(c: &mut Criterion) {
&[user_id, post_id],
|b, &params| {
b.to_async(&rt).iter(|| async {
let profile = TagPost::get_by_id(params[0], Some(params[1]), None, None)
let profile = TagPost::get_by_id(params[0], params[1], None, None)
.await
.unwrap();
criterion::black_box(profile);
Expand Down
Binary file modified docs/images/pubky-nexus-arch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions src/models/post/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ use utoipa::ToSchema;

use super::{Bookmark, PostCounts, PostDetails, PostRelationships};
use crate::models::tag::post::TagPost;
use crate::models::tag::traits::TagCollection;
use crate::models::tag::TagDetails;

/// Represents a Pubky user with relational data including tags, counts, and relationship with a viewer.
#[derive(Serialize, Deserialize, ToSchema, Default)]
pub struct PostView {
pub details: PostDetails,
pub counts: PostCounts,
pub tags: Vec<TagDetails>,
pub tags: TagPost,
pub relationships: PostRelationships,
pub bookmark: Option<Bookmark>,
}
Expand All @@ -31,7 +29,7 @@ impl PostView {
PostCounts::get_by_id(author_id, post_id),
Bookmark::get_by_id(author_id, post_id, viewer_id),
PostRelationships::get_by_id(author_id, post_id),
TagPost::get_by_id(author_id, Some(post_id), max_tags, max_taggers),
TagPost::get_by_id(author_id, post_id, max_tags, max_taggers),
)?;

let details = match details {
Expand Down
109 changes: 102 additions & 7 deletions src/models/tag/post.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,119 @@
use crate::RedisOps;
use crate::db::kv::index::sorted_sets::Sorting;
use crate::models::tag::details::TagDetails;
use crate::queries;
use crate::{db::connectors::neo4j::get_neo4j_graph, RedisOps};
use axum::async_trait;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::ops::Deref;
use utoipa::ToSchema;

use super::traits::TagCollection;

const POST_TAGS_KEY_PARTS: [&str; 2] = ["Posts", "Tag"];

#[derive(Serialize, Deserialize, Debug, Clone, ToSchema, Default)]
pub struct TagPost;
pub struct TagPost(Vec<TagDetails>);

// Implement Deref so TagList can be used like Vec<String>
impl Deref for TagPost {
type Target = Vec<TagDetails>;

fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl RedisOps for TagPost {
async fn prefix() -> String {
String::from("Post:Taggers")
}
}

impl TagCollection for TagPost {
fn get_tag_prefix<'a>() -> [&'a str; 2] {
POST_TAGS_KEY_PARTS
impl TagPost {
fn create_set_key_parts<'a>(user_id: &'a str, post_id: &'a str) -> Vec<&'a str> {
[&POST_TAGS_KEY_PARTS[..], &[user_id, post_id]].concat()
}

pub async fn get_by_id(
user_id: &str,
post_id: &str,
limit_tags: Option<usize>,
limit_taggers: Option<usize>,
) -> Result<Option<TagPost>, Box<dyn std::error::Error + Send + Sync>> {
// TODO: Not sure if this is the place to do or in the endpoint
let limit_tags = limit_tags.unwrap_or(5);
let limit_taggers = limit_taggers.unwrap_or(5);
match Self::try_from_index(user_id, post_id, limit_tags, limit_taggers).await? {
Some(counts) => Ok(Some(counts)),
None => Self::get_from_graph(user_id, post_id).await,
}
}

async fn try_from_index(
user_id: &str,
post_id: &str,
limit_tags: usize,
limit_taggers: usize,
) -> Result<Option<TagPost>, Box<dyn std::error::Error + Send + Sync>> {
let key_parts = Self::create_set_key_parts(user_id, post_id);
match Self::try_from_index_sorted_set(
&key_parts,
None,
None,
None,
Some(limit_tags),
Sorting::Descending,
)
.await?
{
Some(tag_scores) => {
let mut tags = Vec::with_capacity(limit_tags);
for (label, _) in tag_scores.iter() {
tags.push(format!("{}:{}:{}", user_id, post_id, label));
}
let tags_ref: Vec<&str> = tags.iter().map(|label| label.as_str()).collect();
let taggers = Self::try_from_multiple_sets(&tags_ref, Some(limit_taggers)).await?;
let tag_details_list = TagDetails::from_index(tag_scores, taggers);
Ok(Some(TagPost(tag_details_list)))
}
None => Ok(None),
}
}

pub async fn get_from_graph(
user_id: &str,
post_id: &str,
) -> Result<Option<TagPost>, Box<dyn std::error::Error + Send + Sync>> {
let mut result;
{
// We cannot use LIMIT clause because we need all data related
let query = queries::read::post_tags(user_id, post_id);
let graph = get_neo4j_graph()?;

let graph = graph.lock().await;
result = graph.execute(query).await?;
}

if let Some(row) = result.next().await? {
let user_exists: bool = row.get("post_exists").unwrap_or(false);
if user_exists {
let tagged_from: TagPost = row.get("post_tags").unwrap_or_default();
Self::add_to_label_sorted_set(user_id, post_id, &tagged_from).await?;
return Ok(Some(tagged_from));
}
}
Ok(None)
}

async fn add_to_label_sorted_set(
user_id: &str,
post_id: &str,
tags: &[TagDetails],
) -> Result<(), Box<dyn Error + Send + Sync>> {
let (tag_scores, (labels, taggers)) = TagDetails::process_tag_details(tags);

let key_parts = Self::create_set_key_parts(user_id, post_id);
Self::put_index_sorted_set(&key_parts, tag_scores.as_slice()).await?;

Self::put_multiple_set_indexes(&[user_id, post_id], &labels, &taggers).await
}
}
1 change: 1 addition & 0 deletions src/models/tag/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::traits::TagCollection;

const USER_TAGS_KEY_PARTS: [&str; 2] = ["Users", "Tag"];

// Define a newtype wrapper
#[derive(Serialize, Deserialize, Debug, Clone, ToSchema, Default)]
pub struct TagUser;

Expand Down
2 changes: 1 addition & 1 deletion src/reindex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub async fn reindex_post(
PostDetails::get_from_graph(author_id, post_id),
PostCounts::get_from_graph(author_id, post_id),
PostRelationships::get_from_graph(author_id, post_id),
TagPost::get_from_graph(author_id, Some(post_id))
TagPost::get_from_graph(author_id, post_id)
)?;

Ok(())
Expand Down
14 changes: 3 additions & 11 deletions src/routes/v0/post/tags.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::models::tag::post::TagPost;
use crate::models::tag::traits::TagCollection;
use crate::models::tag::TagDetails;
use crate::routes::v0::endpoints::POST_TAGS_ROUTE;
use crate::routes::v0::TagsQuery;
Expand Down Expand Up @@ -28,25 +27,18 @@ use utoipa::OpenApi;
pub async fn post_tags_handler(
Path((user_id, post_id)): Path<(String, String)>,
Query(query): Query<TagsQuery>,
) -> Result<Json<Vec<TagDetails>>> {
) -> Result<Json<TagPost>> {
info!(
"GET {POST_TAGS_ROUTE} user_id:{}, post_id: {}, limit_tags:{:?}, limit_taggers:{:?}",
user_id, post_id, query.limit_tags, query.limit_taggers
);
match TagPost::get_by_id(
&user_id,
Some(&post_id),
query.limit_tags,
query.limit_taggers,
)
.await
{
match TagPost::get_by_id(&user_id, &post_id, query.limit_tags, query.limit_taggers).await {
Ok(Some(tags)) => Ok(Json(tags)),
Ok(None) => Err(Error::UserNotFound { user_id }),
Err(source) => Err(Error::InternalServerError { source }),
}
}

#[derive(OpenApi)]
#[openapi(paths(post_tags_handler), components(schemas(TagDetails)))]
#[openapi(paths(post_tags_handler), components(schemas(TagPost, TagDetails)))]
pub struct PostTagsApiDoc;

0 comments on commit 8f3555b

Please sign in to comment.