-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add graph return control for all ingestion events #253
Merged
Merged
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e830dab
Profile control in PUT post
tipogi edb7c4e
add in PUT events, user profile control. File PUT missing
tipogi 06cdea4
add in DEL event, user profile control. Files missing
tipogi 1b80258
minor changes in file DEL
tipogi 76f95f1
Add note
tipogi bc1be0b
post dependecy control
tipogi 2c6a477
refactor post creation query
tipogi 7c83996
Remove the PostInteraction enum in favor of PostRelationships
tipogi a8a6752
added integration test when the graph is unsync with homeserver
tipogi e2b2b9b
added another test
tipogi df02e70
reviewed watcher tests
tipogi 9e22f2d
small fixes
tipogi b7832c5
deps: bump axum to 0.8.1 (#276)
SHAcollision 2bd53cf
deps: bump serde_json from 1.0.134 to 1.0.135 (#281)
dependabot[bot] efd64d5
be more ideomatic with the query results
tipogi 3ecc287
change query return values to match OperationOutcome enum
tipogi f22b4d5
merge main
tipogi b46c375
rename return value of the query when adding user tags
tipogi 7b290ec
small fixes
tipogi 09d17fb
review fixes
tipogi 5d39d4b
review fixes
tipogi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
use crate::events::uri::ParsedUri; | ||
use crate::models::post::PostRelationships; | ||
use crate::models::{file::FileDetails, post::PostDetails, user::UserDetails}; | ||
use crate::types::DynError; | ||
use neo4rs::{query, Query}; | ||
|
@@ -21,74 +23,94 @@ pub fn create_user(user: &UserDetails) -> Result<Query, DynError> { | |
Ok(query) | ||
} | ||
|
||
// Create a post node | ||
// TODO: DIscuss if it is necessary here or create a URI when we get the post_id, get_posts_details_by_id | ||
pub fn create_post(post: &PostDetails) -> Result<Query, DynError> { | ||
let kind = serde_json::to_string(&post.kind)?; | ||
/// Creates a Cypher query to add or edit a post to the graph database and handles its relationships. | ||
/// # Arguments | ||
/// * `post` - A reference to a `PostDetails` struct containing information about the post to be created or edited | ||
/// * `post_relationships` - A reference to a PostRelationships struct that define relationships | ||
/// for the post (e.g., replies or reposts). | ||
pub fn create_post(post: &PostDetails, post_relationships: &PostRelationships) -> Result<Query, DynError> { | ||
let mut cypher = String::new(); | ||
let mut new_relationships = Vec::new(); | ||
|
||
let query = query( | ||
"MATCH (u:User {id: $author_id}) | ||
// Check if all the dependencies are consistent in the graph | ||
if let Some(_) = &post_relationships.replied { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
cypher.push_str(" | ||
MATCH (reply_parent_author:User {id: $reply_parent_author_id})-[:AUTHORED]->(reply_parent_post:Post {id: $reply_parent_post_id}) | ||
"); | ||
new_relationships.push("MERGE (new_post)-[:REPLIED]->(reply_parent_post)"); | ||
}; | ||
if let Some(_) = &post_relationships.reposted { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
cypher.push_str(" | ||
MATCH (repost_parent_author:User {id: $repost_parent_author_id})-[:AUTHORED]->(repost_parent_post:Post {id: $repost_parent_post_id}) | ||
"); | ||
new_relationships.push("MERGE (new_post)-[:REPOSTED]->(repost_parent_post)"); | ||
} | ||
// Create the new post | ||
cypher.push_str(" | ||
MATCH (author:User {id: $author_id}) | ||
OPTIONAL MATCH (u)-[:AUTHORED]->(existing_post:Post {id: $post_id}) | ||
MERGE (author)-[:AUTHORED]->(new_post:Post {id: $post_id}) | ||
"); | ||
|
||
// Check if post already existed | ||
OPTIONAL MATCH (u)-[:AUTHORED]->(existing:Post {id: $post_id}) | ||
// Add relationships to the query | ||
cypher.push_str(&new_relationships.join("\n")); | ||
|
||
// Write data | ||
MERGE (u)-[:AUTHORED]->(p:Post {id: $post_id}) | ||
SET p.content = $content, | ||
p.indexed_at = $indexed_at, | ||
p.kind = $kind, | ||
p.attachments = $attachments | ||
|
||
// boolean == existed | ||
RETURN existing IS NOT NULL AS boolean;", | ||
) | ||
.param("author_id", post.author.to_string()) | ||
.param("post_id", post.id.to_string()) | ||
.param("content", post.content.to_string()) | ||
.param("indexed_at", post.indexed_at) | ||
.param("kind", kind.trim_matches('"')) | ||
.param( | ||
"attachments", | ||
post.attachments.clone().unwrap_or(vec![] as Vec<String>), | ||
cypher.push_str(" | ||
SET new_post.content = $content, | ||
new_post.indexed_at = $indexed_at, | ||
new_post.kind = $kind, | ||
new_post.attachments = $attachments | ||
RETURN existing_post IS NOT NULL AS boolean" | ||
); | ||
|
||
let kind = serde_json::to_string(&post.kind)?; | ||
|
||
let mut cypher_query = query(&cypher) | ||
.param("author_id", post.author.to_string()) | ||
.param("post_id", post.id.to_string()) | ||
.param("content", post.content.to_string()) | ||
.param("indexed_at", post.indexed_at) | ||
.param("kind", kind.trim_matches('"')) | ||
.param( | ||
"attachments", | ||
post.attachments.clone().unwrap_or(vec![] as Vec<String>), | ||
); | ||
|
||
Ok(query) | ||
} | ||
// Fill up relationships parameters | ||
cypher_query = add_relationship_params( | ||
cypher_query, | ||
&post_relationships.replied, | ||
"reply_parent_author_id", | ||
"reply_parent_post_id", | ||
)?; | ||
|
||
/// Create a reply relationship between two posts | ||
pub fn create_reply_relationship( | ||
author_id: &str, | ||
post_id: &str, | ||
parent_author_id: &str, | ||
parent_post_id: &str, | ||
) -> Query { | ||
query( | ||
"MATCH (parent_author:User {id: $parent_author_id})-[:AUTHORED]->(parent_post:Post {id: $parent_post_id}), | ||
(author:User {id: $author_id})-[:AUTHORED]->(post:Post {id: $post_id}) | ||
MERGE (post)-[:REPLIED]->(parent_post)", | ||
) | ||
.param("author_id", author_id) | ||
.param("post_id", post_id) | ||
.param("parent_author_id", parent_author_id) | ||
.param("parent_post_id", parent_post_id) | ||
// Handle "reposted" relationship | ||
cypher_query = add_relationship_params( | ||
cypher_query, | ||
&post_relationships.reposted, | ||
"repost_parent_author_id", | ||
"repost_parent_post_id", | ||
)?; | ||
|
||
Ok(cypher_query) | ||
} | ||
|
||
/// Create a repost relationship between two posts | ||
pub fn create_repost_relationship( | ||
author_id: &str, | ||
post_id: &str, | ||
reposted_author_id: &str, | ||
reposted_post_id: &str, | ||
) -> Query { | ||
query( | ||
"MATCH (reposted_author:User {id: $reposted_author_id})-[:AUTHORED]->(reposted_post:Post {id: $reposted_post_id}), | ||
(author:User {id: $author_id})-[:AUTHORED]->(post:Post {id: $post_id}) | ||
MERGE (post)-[:REPOSTED]->(reposted_post)", | ||
) | ||
.param("author_id", author_id) | ||
.param("post_id", post_id) | ||
.param("reposted_author_id", reposted_author_id) | ||
.param("reposted_post_id", reposted_post_id) | ||
fn add_relationship_params( | ||
cypher_query: Query, | ||
uri: &Option<String>, | ||
author_param: &str, | ||
post_param: &str, | ||
) -> Result<Query, DynError> { | ||
if let Some(uri) = uri { | ||
let parsed_uri = ParsedUri::try_from(uri.as_str())?; | ||
let parent_author_id = parsed_uri.user_id; | ||
let parent_post_id = parsed_uri.post_id.ok_or("Missing post ID")?; | ||
|
||
return Ok(cypher_query | ||
.param(author_param, parent_author_id.as_str()) | ||
.param(post_param, parent_post_id.as_str())); | ||
} | ||
Ok(cypher_query) | ||
} | ||
|
||
// Create a mentioned relationship between a post and a user | ||
|
@@ -132,7 +154,14 @@ pub fn create_follow(follower_id: &str, followee_id: &str, indexed_at: i64) -> Q | |
pub fn create_mute(user_id: &str, muted_id: &str, indexed_at: i64) -> Query { | ||
query( | ||
"MATCH (user:User {id: $user_id}), (muted:User {id: $muted_id}) | ||
MERGE (user)-[:MUTED {indexed_at: $indexed_at}]->(muted);", | ||
// Check if follow already existed | ||
OPTIONAL MATCH (user)-[existing:MUTED]->(muted) | ||
|
||
MERGE (user)-[r:MUTED]->(muted) | ||
SET r.indexed_at = $indexed_at | ||
|
||
// boolean == existed | ||
RETURN existing IS NOT NULL AS boolean;", | ||
) | ||
.param("user_id", user_id.to_string()) | ||
.param("muted_id", muted_id.to_string()) | ||
|
@@ -148,6 +177,7 @@ pub fn create_post_bookmark( | |
) -> Query { | ||
query( | ||
"MATCH (u:User {id: $user_id}) | ||
// We assume these nodes are already created. If not we would not be able to add a bookmark | ||
MATCH (author:User {id: $author_id})-[:AUTHORED]->(p:Post {id: $post_id}) | ||
|
||
// Check if bookmark already existed | ||
|
@@ -177,8 +207,9 @@ pub fn create_post_tag( | |
indexed_at: i64, | ||
) -> Query { | ||
query( | ||
"MATCH (author:User {id: $author_id})-[:AUTHORED]->(post:Post {id: $post_id}) | ||
MATCH (user:User {id: $user_id}) | ||
"MATCH (user:User {id: $user_id}) | ||
// We assume these nodes are already created. If not we would not be able to add a tag | ||
MATCH (author:User {id: $author_id})-[:AUTHORED]->(post:Post {id: $post_id}) | ||
|
||
// Check if tag already existed | ||
OPTIONAL MATCH (user)-[existing:TAGGED {label: $label}]->(post) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we documented this well? For example, it can be document the implicit usage we are making of the returned type of this function to control for non existing objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could use the following enum