nandi/gleanpublic Fork 0
e130c67
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.

Filter trending articles by user’s preferred languagesUnverified

Julien Robert committed 2026-05-07T21:30:06+02:00 Browse files
e130c67 parent: 041a564
modified internal/cluster/scoring.go +15 -11
@@ -234,6 +234,19 @@ func (e *Engine) ComputeFeedRecommendationsOnDemand(ctx context.Context, userDID
234234 return results, rows.Err()
235235 }
236236
237+func buildLangFilter(languages []string, prefix string) (string, []any) {
238+ if len(languages) == 0 {
239+ return "", nil
240+ }
241+ ph := make([]string, len(languages))
242+ args := make([]any, len(languages))
243+ for i, l := range languages {
244+ ph[i] = "?"
245+ args[i] = l
246+ }
247+ return "AND (" + prefix + "language IN (" + strings.Join(ph, ",") + ") OR " + prefix + "language = '')", args
248+}
249+
237250 func normalizeFeedScores(recs []*FeedRecommendation) {
238251 if len(recs) < 2 {
239252 return
@@ -409,16 +422,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
409422 }
410423 }
411424
412- langFilter := ""
413- langArgs := []any{}
414- if len(languages) > 0 {
415- ph := make([]string, len(languages))
416- for i, l := range languages {
417- ph[i] = "?"
418- langArgs = append(langArgs, l)
419- }
420- langFilter = " AND (a.language IN (" + strings.Join(ph, ",") + ") OR a.language = '')"
421- }
425+ langFilter, langArgs := buildLangFilter(languages, "a.")
422426
423427 query := fmt.Sprintf(`
424428 WITH similar_users AS (
@@ -465,7 +469,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
465469 LEFT JOIN social_likes sl ON sl.feed_url = la.feed_url AND sl.article_url = la.article_url
466470 LEFT JOIN _content_boost cb ON cb.article_id = a.id
467471 LEFT JOIN articles.read_state rs ON rs.article_id = a.id AND rs.user_did = ?
468- WHERE COALESCE(rs.is_read, 0) = 0%s
472+ WHERE COALESCE(rs.is_read, 0) = 0 %s
469473 ORDER BY score DESC, (CASE WHEN a.published > 'now' THEN 1 ELSE 0 END), a.published DESC
470474 LIMIT ?
471475 `, langFilter)
@@ -234,6 +234,19 @@ func (e *Engine) ComputeFeedRecommendationsOnDemand(ctx context.Context, userDID
234 return results, rows.Err()234 return results, rows.Err()
235 }235 }
236 236
237+func buildLangFilter(languages []string, prefix string) (string, []any) {
238+ if len(languages) == 0 {
239+ return "", nil
240+ }
241+ ph := make([]string, len(languages))
242+ args := make([]any, len(languages))
243+ for i, l := range languages {
244+ ph[i] = "?"
245+ args[i] = l
246+ }
247+ return "AND (" + prefix + "language IN (" + strings.Join(ph, ",") + ") OR " + prefix + "language = '')", args
248+}
249+
237 func normalizeFeedScores(recs []*FeedRecommendation) {250 func normalizeFeedScores(recs []*FeedRecommendation) {
238 if len(recs) < 2 {251 if len(recs) < 2 {
239 return252 return
@@ -409,16 +422,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
409 }422 }
410 }423 }
411 424
412- langFilter := ""425+ langFilter, langArgs := buildLangFilter(languages, "a.")
413- langArgs := []any{}
414- if len(languages) > 0 {
415- ph := make([]string, len(languages))
416- for i, l := range languages {
417- ph[i] = "?"
418- langArgs = append(langArgs, l)
419- }
420- langFilter = " AND (a.language IN (" + strings.Join(ph, ",") + ") OR a.language = '')"
421- }
422 426
423 query := fmt.Sprintf(`427 query := fmt.Sprintf(`
424 WITH similar_users AS (428 WITH similar_users AS (
@@ -465,7 +469,7 @@ func (e *Engine) ComputeArticleRecommendationsOnDemand(ctx context.Context, user
465 LEFT JOIN social_likes sl ON sl.feed_url = la.feed_url AND sl.article_url = la.article_url469 LEFT JOIN social_likes sl ON sl.feed_url = la.feed_url AND sl.article_url = la.article_url
466 LEFT JOIN _content_boost cb ON cb.article_id = a.id470 LEFT JOIN _content_boost cb ON cb.article_id = a.id
467 LEFT JOIN articles.read_state rs ON rs.article_id = a.id AND rs.user_did = ?471 LEFT JOIN articles.read_state rs ON rs.article_id = a.id AND rs.user_did = ?
468- WHERE COALESCE(rs.is_read, 0) = 0%s472+ WHERE COALESCE(rs.is_read, 0) = 0 %s
469 ORDER BY score DESC, (CASE WHEN a.published > 'now' THEN 1 ELSE 0 END), a.published DESC473 ORDER BY score DESC, (CASE WHEN a.published > 'now' THEN 1 ELSE 0 END), a.published DESC
470 LIMIT ?474 LIMIT ?
471 `, langFilter)475 `, langFilter)
modified internal/db/social.go +18 -2
@@ -9,6 +9,19 @@ import (
99
1010 var ErrDuplicateLike = errors.New("already liked this article")
1111
12+func buildLangFilter(languages []string, prefix string) (string, []any) {
13+ if len(languages) == 0 {
14+ return "", nil
15+ }
16+ ph := make([]string, len(languages))
17+ args := make([]any, len(languages))
18+ for i, l := range languages {
19+ ph[i] = "?"
20+ args[i] = l
21+ }
22+ return "AND (" + prefix + "language IN (" + strings.Join(ph, ",") + ") OR " + prefix + "language = '')", args
23+}
24+
1225 type Annotation struct {
1326 ID int64
1427 URI string
@@ -367,7 +380,9 @@ type TrendingItem struct {
367380 HasLiked bool
368381 }
369382
370-func (s *ArticleStore) ListTrendingArticlesForUser(ctx context.Context, userDID, since string, limit, offset int) ([]*TrendingItem, error) {
383+func (s *ArticleStore) ListTrendingArticlesForUser(ctx context.Context, userDID, since string, languages []string, limit, offset int) ([]*TrendingItem, error) {
384+ langFilter, langArgs := buildLangFilter(languages, "ar.")
385+
371386 rows, err := s.db.QueryContext(ctx, `
372387 SELECT ar.id, ar.title, COALESCE(ar.url, ''), COALESCE(ar.author, ''),
373388 COALESCE(ar.summary, ''), l.feed_url, COALESCE(f.title, ''),
@@ -388,11 +403,12 @@ func (s *ArticleStore) ListTrendingArticlesForUser(ctx context.Context, userDID,
388403 UNION SELECT ?
389404 UNION SELECT f.target_did FROM follows f WHERE f.user_did = ?
390405 )
406+ `+langFilter+`
391407 GROUP BY ar.id
392408 -- Future-published articles (e.g., scheduled) sort last
393409 ORDER BY like_count DESC, annotation_count DESC, (CASE WHEN ar.published > 'now' THEN 1 ELSE 0 END), ar.published DESC
394410 LIMIT ? OFFSET ?
395- `, since, userDID, since, userDID, userDID, userDID, userDID, userDID, limit, offset)
411+ `, append(append([]any{since, userDID, since, userDID, userDID, userDID, userDID, userDID}, langArgs...), limit, offset)...)
396412 if err != nil {
397413 return nil, err
398414 }
@@ -9,6 +9,19 @@ import (
9 9
10 var ErrDuplicateLike = errors.New("already liked this article")10 var ErrDuplicateLike = errors.New("already liked this article")
11 11
12+func buildLangFilter(languages []string, prefix string) (string, []any) {
13+ if len(languages) == 0 {
14+ return "", nil
15+ }
16+ ph := make([]string, len(languages))
17+ args := make([]any, len(languages))
18+ for i, l := range languages {
19+ ph[i] = "?"
20+ args[i] = l
21+ }
22+ return "AND (" + prefix + "language IN (" + strings.Join(ph, ",") + ") OR " + prefix + "language = '')", args
23+}
24+
12 type Annotation struct {25 type Annotation struct {
13 ID int6426 ID int64
14 URI string27 URI string
@@ -367,7 +380,9 @@ type TrendingItem struct {
367 HasLiked bool380 HasLiked bool
368 }381 }
369 382
370-func (s *ArticleStore) ListTrendingArticlesForUser(ctx context.Context, userDID, since string, limit, offset int) ([]*TrendingItem, error) {383+func (s *ArticleStore) ListTrendingArticlesForUser(ctx context.Context, userDID, since string, languages []string, limit, offset int) ([]*TrendingItem, error) {
384+ langFilter, langArgs := buildLangFilter(languages, "ar.")
385+
371 rows, err := s.db.QueryContext(ctx, `386 rows, err := s.db.QueryContext(ctx, `
372 SELECT ar.id, ar.title, COALESCE(ar.url, ''), COALESCE(ar.author, ''),387 SELECT ar.id, ar.title, COALESCE(ar.url, ''), COALESCE(ar.author, ''),
373 COALESCE(ar.summary, ''), l.feed_url, COALESCE(f.title, ''),388 COALESCE(ar.summary, ''), l.feed_url, COALESCE(f.title, ''),
@@ -388,11 +403,12 @@ func (s *ArticleStore) ListTrendingArticlesForUser(ctx context.Context, userDID,
388 UNION SELECT ?403 UNION SELECT ?
389 UNION SELECT f.target_did FROM follows f WHERE f.user_did = ?404 UNION SELECT f.target_did FROM follows f WHERE f.user_did = ?
390 )405 )
406+ `+langFilter+`
391 GROUP BY ar.id407 GROUP BY ar.id
392 -- Future-published articles (e.g., scheduled) sort last408 -- Future-published articles (e.g., scheduled) sort last
393 ORDER BY like_count DESC, annotation_count DESC, (CASE WHEN ar.published > 'now' THEN 1 ELSE 0 END), ar.published DESC409 ORDER BY like_count DESC, annotation_count DESC, (CASE WHEN ar.published > 'now' THEN 1 ELSE 0 END), ar.published DESC
394 LIMIT ? OFFSET ?410 LIMIT ? OFFSET ?
395- `, since, userDID, since, userDID, userDID, userDID, userDID, userDID, limit, offset)411+ `, append(append([]any{since, userDID, since, userDID, userDID, userDID, userDID, userDID}, langArgs...), limit, offset)...)
396 if err != nil {412 if err != nil {
397 return nil, err413 return nil, err
398 }414 }
modified internal/server/dashboard_handler.go +1 -1
@@ -72,7 +72,7 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
7272
7373 since := time.Now().AddDate(0, 0, -7).Format(time.RFC3339)
7474
75- personalTrending, err := s.dbs.Articles.ListTrendingArticlesForUser(ctx, user.DID, since, 5, 0)
75+ personalTrending, err := s.dbs.Articles.ListTrendingArticlesForUser(ctx, user.DID, since, userLangs, 5, 0)
7676 if err != nil {
7777 s.logger.Warn("failed to list personal trending", "error", err, "did", user.DID)
7878 }
@@ -72,7 +72,7 @@ func (s *Server) handleDashboard(w http.ResponseWriter, r *http.Request) {
72 72
73 since := time.Now().AddDate(0, 0, -7).Format(time.RFC3339)73 since := time.Now().AddDate(0, 0, -7).Format(time.RFC3339)
74 74
75- personalTrending, err := s.dbs.Articles.ListTrendingArticlesForUser(ctx, user.DID, since, 5, 0)75+ personalTrending, err := s.dbs.Articles.ListTrendingArticlesForUser(ctx, user.DID, since, userLangs, 5, 0)
76 if err != nil {76 if err != nil {
77 s.logger.Warn("failed to list personal trending", "error", err, "did", user.DID)77 s.logger.Warn("failed to list personal trending", "error", err, "did", user.DID)
78 }78 }