@@ -103,6 +103,8 @@ pub trait RepoTrait: Sync + Send + 'static {
103103 fn get_recommendations (
104104 & self ,
105105 _pubkey : & PublicKey ,
106+ _min_pagerank : Option < f64 > ,
107+ _limit : Option < usize > ,
106108 ) -> impl std:: future:: Future < Output = Result < Vec < Recommendation > , RepoError > > + std:: marker:: Send
107109 {
108110 async { panic ! ( "Not implemented" ) }
@@ -458,12 +460,29 @@ impl RepoTrait for Repo {
458460 async fn get_recommendations (
459461 & self ,
460462 pubkey : & PublicKey ,
463+ min_pagerank : Option < f64 > ,
464+ limit : Option < usize > ,
461465 ) -> Result < Vec < Recommendation > , RepoError > {
466+ // Validate and set defaults
467+ let min_pagerank = min_pagerank. unwrap_or ( 0.3 ) ;
468+ if min_pagerank < 0.0 {
469+ return Err ( RepoError :: InvalidParameter (
470+ "min_pagerank must be non-negative" . into ( ) ,
471+ ) ) ;
472+ }
473+
474+ let limit = limit. unwrap_or ( 50 ) ;
475+ if limit == 0 {
476+ return Err ( RepoError :: InvalidParameter (
477+ "limit must be greater than 0" . into ( ) ,
478+ ) ) ;
479+ }
480+
462481 let statement = r#"
463482 // Step 1: Get valid target nodes
464483 MATCH (source:User {pubkey: $pubkey_val})
465484 MATCH (target:User)
466- WHERE target.pagerank >= 0.3
485+ WHERE target.pagerank >= $min_pagerank
467486 AND NOT EXISTS {
468487 MATCH (source)-[:FOLLOWS]->(target)
469488 }
@@ -473,19 +492,28 @@ impl RepoTrait for Repo {
473492 CALL gds.nodeSimilarity.filtered.stream('filteredGraph', {
474493 sourceNodeFilter: [id(source)],
475494 targetNodeFilter: targetNodeIds,
476- topK: 10 ,
495+ topK: $limit ,
477496 similarityCutoff: 0.1
478497 })
479498 YIELD node1, node2, similarity
480499 WITH gds.util.asNode(node2) AS targetUser, similarity
500+
501+ // Step 3: Count how many of source's follows also follow the target
502+ OPTIONAL MATCH (source)-[:FOLLOWS]->(mutual:User)-[:FOLLOWS]->(targetUser)
503+ WITH targetUser, similarity, count(DISTINCT mutual) AS mutualFollowCount
504+
481505 RETURN targetUser.pubkey AS target_pubkey,
482506 targetUser.friendly_id AS friendly_id,
483507 similarity,
484- targetUser.pagerank AS pagerank
508+ targetUser.pagerank AS pagerank,
509+ mutualFollowCount
485510 ORDER BY similarity DESC, pagerank DESC;
486511 "# ;
487512
488- let query = query ( statement) . param ( "pubkey_val" , pubkey. to_hex ( ) ) ;
513+ let query = query ( statement)
514+ . param ( "pubkey_val" , pubkey. to_hex ( ) )
515+ . param ( "min_pagerank" , min_pagerank)
516+ . param ( "limit" , limit as i64 ) ;
489517
490518 let mut records = self
491519 . graph
@@ -507,13 +535,28 @@ impl RepoTrait for Repo {
507535 let pagerank: f64 = row. get :: < f64 > ( "pagerank" ) . map_err ( |e| {
508536 RepoError :: deserialization_with_context ( e, "deserializing 'pagerank' field" )
509537 } ) ?;
538+ let mutual_follow_count: i64 = row. get :: < i64 > ( "mutualFollowCount" ) . unwrap_or ( 0 ) ;
539+
540+ // Determine recommendation reason
541+ let reason_type = if similarity > 0.5 {
542+ RecommendationReason :: HighSimilarity
543+ } else if similarity > 0.3 {
544+ RecommendationReason :: ModerateSimularity
545+ } else if mutual_follow_count > 5 {
546+ RecommendationReason :: FollowedByMany
547+ } else if pagerank > 1.0 {
548+ RecommendationReason :: PopularAccount
549+ } else {
550+ RecommendationReason :: ModerateSimularity
551+ } ;
510552
511553 recommendations. push ( Recommendation {
512554 pubkey : PublicKey :: from_hex ( & pubkey)
513555 . map_err ( RepoError :: GetRecommendationsPubkey ) ?,
514556 friendly_id,
515557 similarity,
516558 pagerank,
559+ reason_type,
517560 } ) ;
518561 }
519562
@@ -710,6 +753,9 @@ pub enum RepoError {
710753
711754 #[ error( "Failed to remove pubkey: {0}" ) ]
712755 RemovePubkey ( neo4rs:: Error ) ,
756+
757+ #[ error( "Invalid parameter: {0}" ) ]
758+ InvalidParameter ( String ) ,
713759}
714760
715761impl RepoError {
@@ -724,10 +770,20 @@ impl RepoError {
724770 }
725771}
726772
773+ #[ derive( Debug , Serialize , Clone ) ]
774+ #[ serde( rename_all = "snake_case" ) ]
775+ pub enum RecommendationReason {
776+ HighSimilarity , // Similarity score > 0.5
777+ ModerateSimularity , // Similarity score 0.3-0.5
778+ PopularAccount , // High pagerank but lower similarity
779+ FollowedByMany , // Many of your follows also follow them
780+ }
781+
727782#[ derive( Debug , Serialize , Clone ) ]
728783pub struct Recommendation {
729- pubkey : PublicKey ,
730- friendly_id : String ,
731- similarity : f64 ,
732- pagerank : f64 ,
784+ pub pubkey : PublicKey ,
785+ pub friendly_id : String ,
786+ pub similarity : f64 ,
787+ pub pagerank : f64 ,
788+ pub reason_type : RecommendationReason ,
733789}
0 commit comments