nandi/gleanpublic⑂ Fork 0
⑂ e635425
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/glean.git
git clone ssh://git@rickub.com/nandi/glean.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Fix Jaccard similarity calculation to include incoming followsUnverified

Julien Robert committed 2026-04-21T17:07:54+02:00 Browse files
e635425 parent: e7fd77c
modified internal/cluster/jaccard.go +9 -6
@@ -606,18 +606,21 @@ func (e *Engine) ComputeUserSimilarityForUser(ctx context.Context, userDID strin
606606 followQuery := fmt.Sprintf(`
607607 INSERT INTO user_similarity (user_a, user_b, jaccard, common_feeds, common_likes, common_tags)
608608 SELECT
609- MIN(?, f.target_did),
610- MAX(?, f.target_did),
609+ MIN(?, peer_did),
610+ MAX(?, peer_did),
611611 %g,
612612 0, 0, 0
613- FROM follows f
614- WHERE f.user_did = ? AND f.target_did != ?
615- GROUP BY MIN(?, f.target_did), MAX(?, f.target_did)
613+ FROM (
614+ SELECT target_did AS peer_did FROM follows WHERE user_did = ? AND target_did != ?
615+ UNION
616+ SELECT user_did AS peer_did FROM follows WHERE target_did = ? AND user_did != ?
617+ )
618+ GROUP BY MIN(?, peer_did), MAX(?, peer_did)
616619 ON CONFLICT(user_a, user_b) DO UPDATE SET
617620 jaccard = jaccard + %g
618621 `, e.config.FollowBoost, e.config.FollowBoost)
619622
620- if _, err := tx.ExecContext(ctx, followQuery, userDID, userDID, userDID, userDID, userDID, userDID); err != nil {
623+ if _, err := tx.ExecContext(ctx, followQuery, userDID, userDID, userDID, userDID, userDID, userDID, userDID, userDID); err != nil {
621624 return err
622625 }
623626
@@ -606,18 +606,21 @@ func (e *Engine) ComputeUserSimilarityForUser(ctx context.Context, userDID strin
606 followQuery := fmt.Sprintf(`606 followQuery := fmt.Sprintf(`
607 INSERT INTO user_similarity (user_a, user_b, jaccard, common_feeds, common_likes, common_tags)607 INSERT INTO user_similarity (user_a, user_b, jaccard, common_feeds, common_likes, common_tags)
608 SELECT608 SELECT
609- MIN(?, f.target_did),609+ MIN(?, peer_did),
610- MAX(?, f.target_did),610+ MAX(?, peer_did),
611 %g,611 %g,
612 0, 0, 0612 0, 0, 0
613- FROM follows f613+ FROM (
614- WHERE f.user_did = ? AND f.target_did != ?614+ SELECT target_did AS peer_did FROM follows WHERE user_did = ? AND target_did != ?
615- GROUP BY MIN(?, f.target_did), MAX(?, f.target_did)615+ UNION
616+ SELECT user_did AS peer_did FROM follows WHERE target_did = ? AND user_did != ?
617+ )
618+ GROUP BY MIN(?, peer_did), MAX(?, peer_did)
616 ON CONFLICT(user_a, user_b) DO UPDATE SET619 ON CONFLICT(user_a, user_b) DO UPDATE SET
617 jaccard = jaccard + %g620 jaccard = jaccard + %g
618 `, e.config.FollowBoost, e.config.FollowBoost)621 `, e.config.FollowBoost, e.config.FollowBoost)
619 622
620- if _, err := tx.ExecContext(ctx, followQuery, userDID, userDID, userDID, userDID, userDID, userDID); err != nil {623+ if _, err := tx.ExecContext(ctx, followQuery, userDID, userDID, userDID, userDID, userDID, userDID, userDID, userDID); err != nil {
621 return err624 return err
622 }625 }
623 626
modified internal/cluster/jaccard_test.go +26 -0
@@ -242,6 +242,32 @@ func TestComputeUserSimilarityForUser_WithFollowBoost(t *testing.T) {
242242 assert.Assert(t, jaccard > 0.6, "follow boost should add 0.5 to subscription jaccard, got %f", jaccard)
243243 }
244244
245+func TestComputeUserSimilarityForUser_IncomingFollow(t *testing.T) {
246+ ctx := context.Background()
247+ database := setupClusterTestDB(t)
248+ seedClusterData(t, ctx, database)
249+
250+ _, err := database.ExecContext(ctx, `INSERT INTO follows (user_did, target_did) VALUES (?, ?)`, "did:test:bob", "did:test:alice")
251+ assert.NilError(t, err)
252+
253+ engine := NewEngine(database.DB, slog.Default())
254+ assert.NilError(t, engine.ComputeUserSimilarity(ctx))
255+
256+ var jaccardBefore float64
257+ assert.NilError(t, database.QueryRowContext(ctx,
258+ `SELECT jaccard FROM user_similarity WHERE user_a = ? AND user_b = ?`,
259+ "did:test:alice", "did:test:bob").Scan(&jaccardBefore))
260+
261+ assert.NilError(t, engine.ComputeUserSimilarityForUser(ctx, "did:test:alice"))
262+
263+ var jaccardAfter float64
264+ assert.NilError(t, database.QueryRowContext(ctx,
265+ `SELECT jaccard FROM user_similarity WHERE user_a = ? AND user_b = ?`,
266+ "did:test:alice", "did:test:bob").Scan(&jaccardAfter))
267+
268+ assert.Assert(t, jaccardAfter > 0, "incoming follow boost should survive per-user recomputation, got %f", jaccardAfter)
269+}
270+
245271 func TestComputeRecommendationsForUser(t *testing.T) {
246272 ctx := context.Background()
247273 database := setupClusterTestDB(t)
@@ -242,6 +242,32 @@ func TestComputeUserSimilarityForUser_WithFollowBoost(t *testing.T) {
242 assert.Assert(t, jaccard > 0.6, "follow boost should add 0.5 to subscription jaccard, got %f", jaccard)242 assert.Assert(t, jaccard > 0.6, "follow boost should add 0.5 to subscription jaccard, got %f", jaccard)
243 }243 }
244 244
245+func TestComputeUserSimilarityForUser_IncomingFollow(t *testing.T) {
246+ ctx := context.Background()
247+ database := setupClusterTestDB(t)
248+ seedClusterData(t, ctx, database)
249+
250+ _, err := database.ExecContext(ctx, `INSERT INTO follows (user_did, target_did) VALUES (?, ?)`, "did:test:bob", "did:test:alice")
251+ assert.NilError(t, err)
252+
253+ engine := NewEngine(database.DB, slog.Default())
254+ assert.NilError(t, engine.ComputeUserSimilarity(ctx))
255+
256+ var jaccardBefore float64
257+ assert.NilError(t, database.QueryRowContext(ctx,
258+ `SELECT jaccard FROM user_similarity WHERE user_a = ? AND user_b = ?`,
259+ "did:test:alice", "did:test:bob").Scan(&jaccardBefore))
260+
261+ assert.NilError(t, engine.ComputeUserSimilarityForUser(ctx, "did:test:alice"))
262+
263+ var jaccardAfter float64
264+ assert.NilError(t, database.QueryRowContext(ctx,
265+ `SELECT jaccard FROM user_similarity WHERE user_a = ? AND user_b = ?`,
266+ "did:test:alice", "did:test:bob").Scan(&jaccardAfter))
267+
268+ assert.Assert(t, jaccardAfter > 0, "incoming follow boost should survive per-user recomputation, got %f", jaccardAfter)
269+}
270+
245 func TestComputeRecommendationsForUser(t *testing.T) {271 func TestComputeRecommendationsForUser(t *testing.T) {
246 ctx := context.Background()272 ctx := context.Background()
247 database := setupClusterTestDB(t)273 database := setupClusterTestDB(t)