Exclude subscribed feeds from recommendations and unscope article queriesUnverified
8b00ca2 parent: 973a7c3 modified
internal/cluster/recommender.go +2 -1 | @@ -54,9 +54,10 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim | ||
| 54 | 54 | FROM user_feed_recommendations r |
| 55 | 55 | JOIN feeds f ON f.feed_url = r.feed_url |
| 56 | 56 | WHERE r.user_did = ? |
| 57 | + AND r.feed_url NOT IN (SELECT feed_url FROM subscriptions WHERE user_did = ?) | |
| 57 | 58 | ORDER BY r.score DESC |
| 58 | 59 | LIMIT ? |
| 59 | - `, userDID, limit) | |
| 60 | + `, userDID, userDID, limit) | |
| 60 | 61 | if err != nil { |
| 61 | 62 | return nil, err |
| 62 | 63 | } |
| @@ -54,9 +54,10 @@ func (e *Engine) GetFeedRecommendations(ctx context.Context, userDID string, lim | |||
| 54 | FROM user_feed_recommendations r | 54 | FROM user_feed_recommendations r |
| 55 | JOIN feeds f ON f.feed_url = r.feed_url | 55 | 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 DESC | 58 | 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, err | 62 | 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) { | ||
| 64 | 64 | } |
| 65 | 65 | |
| 66 | 66 | 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 | |
| 78 | 69 | |
| 79 | 70 | 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} | |
| 82 | 93 | } |
| 83 | 94 | |
| 84 | 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 | 117 | 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 | |
| 118 | 120 | |
| 119 | 121 | 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} | |
| 122 | 144 | } |
| 123 | 145 | |
| 124 | 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 | 168 | 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 | |
| 158 | 171 | |
| 159 | 172 | 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} | |
| 162 | 195 | } |
| 163 | 196 | |
| 164 | 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 | 241 | INSERT INTO read_state (user_did, article_id, is_read, read_at) |
| 209 | 242 | SELECT ?, a.id, 1, CURRENT_TIMESTAMP |
| 210 | 243 | FROM articles a |
| 211 | - JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ? | |
| 212 | 244 | WHERE a.feed_url = ? |
| 213 | 245 | ON CONFLICT(user_did, article_id) DO UPDATE SET |
| 214 | 246 | is_read = 1, read_at = CURRENT_TIMESTAMP |
| 215 | - `, userDID, userDID, feedURL) | |
| 247 | + `, userDID, feedURL) | |
| 216 | 248 | return err |
| 217 | 249 | } |
| 218 | 250 | |
| @@ -249,10 +281,9 @@ func (db *DB) GetUnreadCount(ctx context.Context, userDID, feedURL string) (int, | ||
| 249 | 281 | err := db.QueryRowContext(ctx, ` |
| 250 | 282 | SELECT COUNT(*) |
| 251 | 283 | FROM articles a |
| 252 | - JOIN subscriptions s ON a.feed_url = s.feed_url AND s.user_did = ? | |
| 253 | 284 | LEFT JOIN read_state r ON r.user_did = ? AND r.article_id = a.id |
| 254 | 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 | 287 | return count, err |
| 257 | 288 | } |
| 258 | 289 | 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_TIMESTAMP | 242 | SELECT ?, a.id, 1, CURRENT_TIMESTAMP |
| 210 | FROM articles a | 243 | 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 SET | 245 | ON CONFLICT(user_did, article_id) DO UPDATE SET |
| 214 | is_read = 1, read_at = CURRENT_TIMESTAMP | 246 | is_read = 1, read_at = CURRENT_TIMESTAMP |
| 215 | - `, userDID, userDID, feedURL) | 247 | + `, userDID, feedURL) |
| 216 | return err | 248 | 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 a | 283 | 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.id | 284 | 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, err | 287 | return count, err |
| 257 | } | 288 | } |
| 258 | err := db.QueryRowContext(ctx, ` | 289 | err := db.QueryRowContext(ctx, ` |