nandi/gleanpublic⑂ Fork 0
⑂ 8b00ca2
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.

Exclude subscribed feeds from recommendations and unscope article queriesUnverified

Julien Robert committed 2026-04-21T17:47:11+02:00 Browse files
8b00ca2 parent: 973a7c3
modified internal/cluster/recommender.go +2 -1
@@ -54,9 +54,10 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim
5454 FROM user_feed_recommendations r
5555 JOIN feeds f ON f.feed_url = r.feed_url
5656 WHERE r.user_did = ?
57+ AND r.feed_url NOT IN (SELECT feed_url FROM subscriptions WHERE user_did = ?)
5758 ORDER BY r.score DESC
5859 LIMIT ?
59- `, userDID, limit)
60+ `, userDID, userDID, limit)
6061 if err != nil {
6162 return nil, err
6263 }
@@ -54,9 +54,10 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim
54 FROM user_feed_recommendations r54 FROM user_feed_recommendations r
55 JOIN feeds f ON f.feed_url = r.feed_url55 JOIN feeds f ON f.feed_url = r.feed_url
56 WHERE r.user_did = ?56 WHERE r.user_did = ?
57+ AND r.feed_url NOT IN (SELECT feed_url FROM subscriptions WHERE user_did = ?)
57 ORDER BY r.score DESC58 ORDER BY r.score DESC
58 LIMIT ?59 LIMIT ?
59- `, userDID, limit)60+ `, userDID, userDID, limit)
60 if err != nil {61 if err != nil {
61 return nil, err62 return nil, err
62 }63 }
modified internal/db/article.go +74 -43
@@ -64,21 +64,32 @@ func (db *DB) GetArticle(ctx context.Context, id int64) (*Article, error) {
6464 }
6565
6666 func (db *DB) ListArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {
67- query := `
68- SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
69- a.published, a.updated, a.fetched_at,
70- COALESCE(r.is_read, 0)
71- FROM articles a
72- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
73- LEFT JOIN feeds f ON a.feed_url = f.feed_url
74- LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
75- WHERE 1=1
76- `
77- args := []any{userDID, userDID}
67+ var query string
68+ var args []any
7869
7970 if feedURL != "" {
80- query += ` AND a.feed_url = ?`
81- args = append(args, feedURL)
71+ query = `
72+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
73+ a.published, a.updated, a.fetched_at,
74+ COALESCE(r.is_read, 0)
75+ FROM articles a
76+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
77+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
78+ WHERE a.feed_url = ?
79+ `
80+ args = []any{userDID, feedURL}
81+ } else {
82+ query = `
83+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
84+ a.published, a.updated, a.fetched_at,
85+ COALESCE(r.is_read, 0)
86+ FROM articles a
87+ JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
88+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
89+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
90+ WHERE 1=1
91+ `
92+ args = []any{userDID, userDID}
8293 }
8394
8495 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`
@@ -104,21 +115,32 @@ func (db *DB) ListArticles(ctx context.Context, userDID, feedURL string, limit,
104115 }
105116
106117 func (db *DB) ListUnreadArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {
107- query := `
108- SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
109- a.published, a.updated, a.fetched_at,
110- COALESCE(r.is_read, 0)
111- FROM articles a
112- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
113- LEFT JOIN feeds f ON a.feed_url = f.feed_url
114- LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
115- WHERE (r.is_read = 0 OR r.is_read IS NULL)
116- `
117- args := []any{userDID, userDID}
118+ var query string
119+ var args []any
118120
119121 if feedURL != "" {
120- query += ` AND a.feed_url = ?`
121- args = append(args, feedURL)
122+ query = `
123+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
124+ a.published, a.updated, a.fetched_at,
125+ COALESCE(r.is_read, 0)
126+ FROM articles a
127+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
128+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
129+ WHERE a.feed_url = ? AND (r.is_read = 0 OR r.is_read IS NULL)
130+ `
131+ args = []any{userDID, feedURL}
132+ } else {
133+ query = `
134+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
135+ a.published, a.updated, a.fetched_at,
136+ COALESCE(r.is_read, 0)
137+ FROM articles a
138+ JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
139+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
140+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
141+ WHERE (r.is_read = 0 OR r.is_read IS NULL)
142+ `
143+ args = []any{userDID, userDID}
122144 }
123145
124146 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`
@@ -144,21 +166,32 @@ func (db *DB) ListUnreadArticles(ctx context.Context, userDID, feedURL string, l
144166 }
145167
146168 func (db *DB) ListReadArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {
147- query := `
148- SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
149- a.published, a.updated, a.fetched_at,
150- COALESCE(r.is_read, 0)
151- FROM articles a
152- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
153- LEFT JOIN feeds f ON a.feed_url = f.feed_url
154- JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
155- WHERE r.is_read = 1
156- `
157- args := []any{userDID, userDID}
169+ var query string
170+ var args []any
158171
159172 if feedURL != "" {
160- query += ` AND a.feed_url = ?`
161- args = append(args, feedURL)
173+ query = `
174+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
175+ a.published, a.updated, a.fetched_at,
176+ COALESCE(r.is_read, 0)
177+ FROM articles a
178+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
179+ JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
180+ WHERE r.is_read = 1 AND a.feed_url = ?
181+ `
182+ args = []any{userDID, feedURL}
183+ } else {
184+ query = `
185+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
186+ a.published, a.updated, a.fetched_at,
187+ COALESCE(r.is_read, 0)
188+ FROM articles a
189+ JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
190+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
191+ JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
192+ WHERE r.is_read = 1
193+ `
194+ args = []any{userDID, userDID}
162195 }
163196
164197 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`
@@ -208,11 +241,10 @@ func (db *DB) MarkAllRead(ctx context.Context, userDID, feedURL string) error {
208241 INSERT INTO read_state (user_did, article_id, is_read, read_at)
209242 SELECT ?, a.id, 1, CURRENT_TIMESTAMP
210243 FROM articles a
211- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
212244 WHERE a.feed_url = ?
213245 ON CONFLICT(user_did, article_id) DO UPDATE SET
214246 is_read = 1, read_at = CURRENT_TIMESTAMP
215- `, userDID, userDID, feedURL)
247+ `, userDID, feedURL)
216248 return err
217249 }
218250
@@ -249,10 +281,9 @@ func (db *DB) GetUnreadCount(ctx context.Context, userDID, feedURL string) (int,
249281 err := db.QueryRowContext(ctx, `
250282 SELECT COUNT(*)
251283 FROM articles a
252- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
253284 LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
254285 WHERE a.feed_url = ? AND (r.is_read = 0 OR r.is_read IS NULL)
255- `, userDID, userDID, feedURL).Scan(&count)
286+ `, userDID, feedURL).Scan(&count)
256287 return count, err
257288 }
258289 err := db.QueryRowContext(ctx, `
@@ -64,21 +64,32 @@ func (db *DB) GetArticle(ctx context.Context, id int64) (*Article, error) {
64 }64 }
65 65
66 func (db *DB) ListArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {66 func (db *DB) ListArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {
67- query := `67+ var query string
68- SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,68+ var args []any
69- a.published, a.updated, a.fetched_at,
70- COALESCE(r.is_read, 0)
71- FROM articles a
72- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
73- LEFT JOIN feeds f ON a.feed_url = f.feed_url
74- LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
75- WHERE 1=1
76- `
77- args := []any{userDID, userDID}
78 69
79 if feedURL != "" {70 if feedURL != "" {
80- query += ` AND a.feed_url = ?`71+ query = `
81- args = append(args, feedURL)72+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
73+ a.published, a.updated, a.fetched_at,
74+ COALESCE(r.is_read, 0)
75+ FROM articles a
76+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
77+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
78+ WHERE a.feed_url = ?
79+ `
80+ args = []any{userDID, feedURL}
81+ } else {
82+ query = `
83+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
84+ a.published, a.updated, a.fetched_at,
85+ COALESCE(r.is_read, 0)
86+ FROM articles a
87+ JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
88+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
89+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
90+ WHERE 1=1
91+ `
92+ args = []any{userDID, userDID}
82 }93 }
83 94
84 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`95 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`
@@ -104,21 +115,32 @@ func (db *DB) ListArticles(ctx context.Context, userDID, feedURL string, limit,
104 }115 }
105 116
106 func (db *DB) ListUnreadArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {117 func (db *DB) ListUnreadArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {
107- query := `118+ var query string
108- SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,119+ var args []any
109- a.published, a.updated, a.fetched_at,
110- COALESCE(r.is_read, 0)
111- FROM articles a
112- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
113- LEFT JOIN feeds f ON a.feed_url = f.feed_url
114- LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
115- WHERE (r.is_read = 0 OR r.is_read IS NULL)
116- `
117- args := []any{userDID, userDID}
118 120
119 if feedURL != "" {121 if feedURL != "" {
120- query += ` AND a.feed_url = ?`122+ query = `
121- args = append(args, feedURL)123+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
124+ a.published, a.updated, a.fetched_at,
125+ COALESCE(r.is_read, 0)
126+ FROM articles a
127+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
128+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
129+ WHERE a.feed_url = ? AND (r.is_read = 0 OR r.is_read IS NULL)
130+ `
131+ args = []any{userDID, feedURL}
132+ } else {
133+ query = `
134+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
135+ a.published, a.updated, a.fetched_at,
136+ COALESCE(r.is_read, 0)
137+ FROM articles a
138+ JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
139+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
140+ LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
141+ WHERE (r.is_read = 0 OR r.is_read IS NULL)
142+ `
143+ args = []any{userDID, userDID}
122 }144 }
123 145
124 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`146 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`
@@ -144,21 +166,32 @@ func (db *DB) ListUnreadArticles(ctx context.Context, userDID, feedURL string, l
144 }166 }
145 167
146 func (db *DB) ListReadArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {168 func (db *DB) ListReadArticles(ctx context.Context, userDID, feedURL string, limit, offset int) ([]*Article, error) {
147- query := `169+ var query string
148- SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,170+ var args []any
149- a.published, a.updated, a.fetched_at,
150- COALESCE(r.is_read, 0)
151- FROM articles a
152- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
153- LEFT JOIN feeds f ON a.feed_url = f.feed_url
154- JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
155- WHERE r.is_read = 1
156- `
157- args := []any{userDID, userDID}
158 171
159 if feedURL != "" {172 if feedURL != "" {
160- query += ` AND a.feed_url = ?`173+ query = `
161- args = append(args, feedURL)174+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
175+ a.published, a.updated, a.fetched_at,
176+ COALESCE(r.is_read, 0)
177+ FROM articles a
178+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
179+ JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
180+ WHERE r.is_read = 1 AND a.feed_url = ?
181+ `
182+ args = []any{userDID, feedURL}
183+ } else {
184+ query = `
185+ SELECT a.id, a.feed_url, COALESCE(f.title, ''), f.favicon_url, a.guid, a.title, a.url, a.author, a.summary, a.content,
186+ a.published, a.updated, a.fetched_at,
187+ COALESCE(r.is_read, 0)
188+ FROM articles a
189+ JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
190+ LEFT JOIN feeds f ON a.feed_url = f.feed_url
191+ JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
192+ WHERE r.is_read = 1
193+ `
194+ args = []any{userDID, userDID}
162 }195 }
163 196
164 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`197 query += ` ORDER BY a.published DESC LIMIT ? OFFSET ?`
@@ -208,11 +241,10 @@ func (db *DB) MarkAllRead(ctx context.Context, userDID, feedURL string) error {
208 INSERT INTO read_state (user_did, article_id, is_read, read_at)241 INSERT INTO read_state (user_did, article_id, is_read, read_at)
209 SELECT ?, a.id, 1, CURRENT_TIMESTAMP242 SELECT ?, a.id, 1, CURRENT_TIMESTAMP
210 FROM articles a243 FROM articles a
211- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
212 WHERE a.feed_url = ?244 WHERE a.feed_url = ?
213 ON CONFLICT(user_did, article_id) DO UPDATE SET245 ON CONFLICT(user_did, article_id) DO UPDATE SET
214 is_read = 1, read_at = CURRENT_TIMESTAMP246 is_read = 1, read_at = CURRENT_TIMESTAMP
215- `, userDID, userDID, feedURL)247+ `, userDID, feedURL)
216 return err248 return err
217 }249 }
218 250
@@ -249,10 +281,9 @@ func (db *DB) GetUnreadCount(ctx context.Context, userDID, feedURL string) (int,
249 err := db.QueryRowContext(ctx, `281 err := db.QueryRowContext(ctx, `
250 SELECT COUNT(*)282 SELECT COUNT(*)
251 FROM articles a283 FROM articles a
252- JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ?
253 LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id284 LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id
254 WHERE a.feed_url = ? AND (r.is_read = 0 OR r.is_read IS NULL)285 WHERE a.feed_url = ? AND (r.is_read = 0 OR r.is_read IS NULL)
255- `, userDID, userDID, feedURL).Scan(&count)286+ `, userDID, feedURL).Scan(&count)
256 return count, err287 return count, err
257 }288 }
258 err := db.QueryRowContext(ctx, `289 err := db.QueryRowContext(ctx, `