Monorepo for Tangled tangled.org

appview/db: more flexible tables #996

open opened by boltless.me targeting master from sl/sqkrqopzkvoo

migrate tables: stars, reactions, follows, public_keys

Two major changes:

  1. Remove autoincrement id for these tables.

AUTOINCREMENT primary key does not help much for these tables and only introduces slice performance overhead. Use default rowid with non-autoincrement integer instead.

  1. Remove unique constraints other than (did, rkey)

We cannot block users creating non-unique atproto records. Appview needs to handle those properly. For example, if user unstar a repo, appview should delete all existing star records pointing to that repo.

To allow this, remove all constraints other than (did, rkey).

Minor changes done while migrating tables:

  • rename thread_at in reactions to subject_at to match with other tables
  • follow common column names like did and created
  • allow self-follow (similar reason to 2nd major change. we should block it from service layer instead)

Signed-off-by: Seongmin Lee git@boltless.me

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:xasnlahkri4ewmbuzly2rlc5/sh.tangled.repo.pull/3mcsfwwhtpn22
+140 -30
Diff #1
+110
appview/db/db.go
··· 3 import ( 4 "context" 5 "database/sql" 6 "log/slog" 7 "strings" 8 ··· 1204 return err 1205 }) 1206 1207 return &DB{ 1208 db, 1209 logger,
··· 3 import ( 4 "context" 5 "database/sql" 6 + "fmt" 7 "log/slog" 8 "strings" 9 ··· 1205 return err 1206 }) 1207 1208 + // several changes here 1209 + // 1. remove autoincrement id for these tables 1210 + // 2. remove unique constraints other than (did, rkey) to handle non-unique atproto records 1211 + // 3. add generated at_uri field 1212 + // 1213 + // see comments below and commit message for details 1214 + orm.RunMigration(conn, logger, "flexible-stars-reactions-follows-public_keys", func(tx *sql.Tx) error { 1215 + // - add at_uri 1216 + // - remove unique constraint (did, subject_at) 1217 + if _, err := tx.Exec(` 1218 + create table stars_new ( 1219 + did text not null, 1220 + rkey text not null, 1221 + at_uri text generated always as ('at://' || did || '/' || 'sh.tangled.feed.star' || '/' || rkey) stored, 1222 + 1223 + subject_at text not null, 1224 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1225 + 1226 + unique(did, rkey) 1227 + ); 1228 + 1229 + insert into stars_new (did, rkey, subject_at, created) 1230 + select did, rkey, subject_at, created from stars; 1231 + 1232 + drop table stars; 1233 + alter table stars_new rename to stars; 1234 + `); err != nil { 1235 + return fmt.Errorf("migrating stars: %w", err) 1236 + } 1237 + 1238 + // - add at_uri 1239 + // - reacted_by_did -> did 1240 + // - thread_at -> subject_at 1241 + // - remove unique constraint 1242 + if _, err := tx.Exec(` 1243 + create table reactions_new ( 1244 + did text not null, 1245 + rkey text not null, 1246 + at_uri text generated always as ('at://' || did || '/' || 'sh.tangled.feed.reaction' || '/' || rkey) stored, 1247 + 1248 + subject_at text not null, 1249 + kind text not null, 1250 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1251 + 1252 + unique(did, rkey) 1253 + ); 1254 + 1255 + insert into reactions_new (did, rkey, subject_at, kind, created) 1256 + select reacted_by_did, rkey, thread_at, kind, created from reactions; 1257 + 1258 + drop table reactions; 1259 + alter table reactions_new rename to reactions; 1260 + `); err != nil { 1261 + return fmt.Errorf("migrating reactions: %w", err) 1262 + } 1263 + 1264 + // - add at_uri column 1265 + // - user_did -> did 1266 + // - followed_at -> created 1267 + // - remove unique constraint 1268 + // - remove check constraint 1269 + if _, err := tx.Exec(` 1270 + create table follows_new ( 1271 + did text not null, 1272 + rkey text not null, 1273 + at_uri text generated always as ('at://' || did || '/' || 'sh.tangled.graph.follow' || '/' || rkey) stored, 1274 + 1275 + subject_did text not null, 1276 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1277 + 1278 + unique(did, rkey) 1279 + ); 1280 + 1281 + insert into follows_new (did, rkey, subject_did, created) 1282 + select user_did, rkey, subject_did, followed_at from follows; 1283 + 1284 + drop table follows; 1285 + alter table follows_new rename to follows; 1286 + `); err != nil { 1287 + return fmt.Errorf("migrating follows: %w", err) 1288 + } 1289 + 1290 + // - add at_uri column 1291 + // - remove foreign key relationship from repos 1292 + if _, err := tx.Exec(` 1293 + create table public_keys_new ( 1294 + did text not null, 1295 + rkey text not null, 1296 + at_uri text generated always as ('at://' || did || '/' || 'sh.tangled.publicKey' || '/' || rkey) stored, 1297 + 1298 + name text not null, 1299 + key text not null, 1300 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1301 + 1302 + unique(did, rkey) 1303 + ); 1304 + 1305 + insert into public_keys_new (did, rkey, name, key, created) 1306 + select did, rkey, name, key, created from public_keys; 1307 + 1308 + drop table public_keys; 1309 + alter table public_keys_new rename to public_keys; 1310 + `); err != nil { 1311 + return fmt.Errorf("migrating public_keys: %w", err) 1312 + } 1313 + 1314 + return nil 1315 + }) 1316 + 1317 return &DB{ 1318 db, 1319 logger,
+12 -12
appview/db/follow.go
··· 11 ) 12 13 func AddFollow(e Execer, follow *models.Follow) error { 14 - query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)` 15 _, err := e.Exec(query, follow.UserDid, follow.SubjectDid, follow.Rkey) 16 return err 17 } 18 19 // Get a follow record 20 func GetFollow(e Execer, userDid, subjectDid string) (*models.Follow, error) { 21 - query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?` 22 row := e.QueryRow(query, userDid, subjectDid) 23 24 var follow models.Follow ··· 41 42 // Remove a follow 43 func DeleteFollow(e Execer, userDid, subjectDid string) error { 44 - _, err := e.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid) 45 return err 46 } 47 48 // Remove a follow 49 func DeleteFollowByRkey(e Execer, userDid, rkey string) error { 50 - _, err := e.Exec(`delete from follows where user_did = ? and rkey = ?`, userDid, rkey) 51 return err 52 } 53 ··· 56 err := e.QueryRow( 57 `SELECT 58 COUNT(CASE WHEN subject_did = ? THEN 1 END) AS followers, 59 - COUNT(CASE WHEN user_did = ? THEN 1 END) AS following 60 FROM follows;`, did, did).Scan(&followers, &following) 61 if err != nil { 62 return models.FollowStats{}, err ··· 96 group by subject_did 97 ) f 98 full outer join ( 99 - select user_did as did, count(*) as following 100 from follows 101 - where user_did in (%s) 102 - group by user_did 103 ) g on f.did = g.did`, 104 placeholderStr, placeholderStr) 105 ··· 156 } 157 158 query := fmt.Sprintf( 159 - `select user_did, subject_did, followed_at, rkey 160 from follows 161 %s 162 - order by followed_at desc 163 %s 164 `, whereClause, limitClause) 165 ··· 198 } 199 200 func GetFollowing(e Execer, did string) ([]models.Follow, error) { 201 - return GetFollows(e, 0, orm.FilterEq("user_did", did)) 202 } 203 204 func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) { ··· 239 query := fmt.Sprintf(` 240 SELECT subject_did 241 FROM follows 242 - WHERE user_did = ? AND subject_did IN (%s) 243 `, strings.Join(placeholders, ",")) 244 245 rows, err := e.Query(query, args...)
··· 11 ) 12 13 func AddFollow(e Execer, follow *models.Follow) error { 14 + query := `insert or ignore into follows (did, subject_did, rkey) values (?, ?, ?)` 15 _, err := e.Exec(query, follow.UserDid, follow.SubjectDid, follow.Rkey) 16 return err 17 } 18 19 // Get a follow record 20 func GetFollow(e Execer, userDid, subjectDid string) (*models.Follow, error) { 21 + query := `select did, subject_did, created, rkey from follows where did = ? and subject_did = ?` 22 row := e.QueryRow(query, userDid, subjectDid) 23 24 var follow models.Follow ··· 41 42 // Remove a follow 43 func DeleteFollow(e Execer, userDid, subjectDid string) error { 44 + _, err := e.Exec(`delete from follows where did = ? and subject_did = ?`, userDid, subjectDid) 45 return err 46 } 47 48 // Remove a follow 49 func DeleteFollowByRkey(e Execer, userDid, rkey string) error { 50 + _, err := e.Exec(`delete from follows where did = ? and rkey = ?`, userDid, rkey) 51 return err 52 } 53 ··· 56 err := e.QueryRow( 57 `SELECT 58 COUNT(CASE WHEN subject_did = ? THEN 1 END) AS followers, 59 + COUNT(CASE WHEN did = ? THEN 1 END) AS following 60 FROM follows;`, did, did).Scan(&followers, &following) 61 if err != nil { 62 return models.FollowStats{}, err ··· 96 group by subject_did 97 ) f 98 full outer join ( 99 + select did as did, count(*) as following 100 from follows 101 + where did in (%s) 102 + group by did 103 ) g on f.did = g.did`, 104 placeholderStr, placeholderStr) 105 ··· 156 } 157 158 query := fmt.Sprintf( 159 + `select did, subject_did, created, rkey 160 from follows 161 %s 162 + order by created desc 163 %s 164 `, whereClause, limitClause) 165 ··· 198 } 199 200 func GetFollowing(e Execer, did string) ([]models.Follow, error) { 201 + return GetFollows(e, 0, orm.FilterEq("did", did)) 202 } 203 204 func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) { ··· 239 query := fmt.Sprintf(` 240 SELECT subject_did 241 FROM follows 242 + WHERE did = ? AND subject_did IN (%s) 243 `, strings.Join(placeholders, ",")) 244 245 rows, err := e.Query(query, args...)
+17 -17
appview/db/reaction.go
··· 8 "tangled.org/core/appview/models" 9 ) 10 11 - func AddReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind, rkey string) error { 12 - query := `insert or ignore into reactions (reacted_by_did, thread_at, kind, rkey) values (?, ?, ?, ?)` 13 - _, err := e.Exec(query, reactedByDid, threadAt, kind, rkey) 14 return err 15 } 16 17 // Get a reaction record 18 - func GetReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind) (*models.Reaction, error) { 19 query := ` 20 - select reacted_by_did, thread_at, created, rkey 21 from reactions 22 - where reacted_by_did = ? and thread_at = ? and kind = ?` 23 - row := e.QueryRow(query, reactedByDid, threadAt, kind) 24 25 var reaction models.Reaction 26 var created string ··· 41 } 42 43 // Remove a reaction 44 - func DeleteReaction(e Execer, reactedByDid string, threadAt syntax.ATURI, kind models.ReactionKind) error { 45 - _, err := e.Exec(`delete from reactions where reacted_by_did = ? and thread_at = ? and kind = ?`, reactedByDid, threadAt, kind) 46 return err 47 } 48 49 // Remove a reaction 50 - func DeleteReactionByRkey(e Execer, reactedByDid string, rkey string) error { 51 - _, err := e.Exec(`delete from reactions where reacted_by_did = ? and rkey = ?`, reactedByDid, rkey) 52 return err 53 } 54 55 - func GetReactionCount(e Execer, threadAt syntax.ATURI, kind models.ReactionKind) (int, error) { 56 count := 0 57 err := e.QueryRow( 58 - `select count(reacted_by_did) from reactions where thread_at = ? and kind = ?`, threadAt, kind).Scan(&count) 59 if err != nil { 60 return 0, err 61 } 62 return count, nil 63 } 64 65 - func GetReactionMap(e Execer, userLimit int, threadAt syntax.ATURI) (map[models.ReactionKind]models.ReactionDisplayData, error) { 66 query := ` 67 - select kind, reacted_by_did, 68 row_number() over (partition by kind order by created asc) as rn, 69 count(*) over (partition by kind) as total 70 from reactions 71 - where thread_at = ? 72 order by kind, created asc` 73 74 - rows, err := e.Query(query, threadAt) 75 if err != nil { 76 return nil, err 77 }
··· 8 "tangled.org/core/appview/models" 9 ) 10 11 + func AddReaction(e Execer, did string, subjectAt syntax.ATURI, kind models.ReactionKind, rkey string) error { 12 + query := `insert or ignore into reactions (did, subject_at, kind, rkey) values (?, ?, ?, ?)` 13 + _, err := e.Exec(query, did, subjectAt, kind, rkey) 14 return err 15 } 16 17 // Get a reaction record 18 + func GetReaction(e Execer, did string, subjectAt syntax.ATURI, kind models.ReactionKind) (*models.Reaction, error) { 19 query := ` 20 + select did, subject_at, created, rkey 21 from reactions 22 + where did = ? and subject_at = ? and kind = ?` 23 + row := e.QueryRow(query, did, subjectAt, kind) 24 25 var reaction models.Reaction 26 var created string ··· 41 } 42 43 // Remove a reaction 44 + func DeleteReaction(e Execer, did string, subjectAt syntax.ATURI, kind models.ReactionKind) error { 45 + _, err := e.Exec(`delete from reactions where did = ? and subject_at = ? and kind = ?`, did, subjectAt, kind) 46 return err 47 } 48 49 // Remove a reaction 50 + func DeleteReactionByRkey(e Execer, did string, rkey string) error { 51 + _, err := e.Exec(`delete from reactions where did = ? and rkey = ?`, did, rkey) 52 return err 53 } 54 55 + func GetReactionCount(e Execer, subjectAt syntax.ATURI, kind models.ReactionKind) (int, error) { 56 count := 0 57 err := e.QueryRow( 58 + `select count(did) from reactions where subject_at = ? and kind = ?`, subjectAt, kind).Scan(&count) 59 if err != nil { 60 return 0, err 61 } 62 return count, nil 63 } 64 65 + func GetReactionMap(e Execer, userLimit int, subjectAt syntax.ATURI) (map[models.ReactionKind]models.ReactionDisplayData, error) { 66 query := ` 67 + select kind, did, 68 row_number() over (partition by kind order by created asc) as rn, 69 count(*) over (partition by kind) as total 70 from reactions 71 + where subject_at = ? 72 order by kind, created asc` 73 74 + rows, err := e.Query(query, subjectAt) 75 if err != nil { 76 return nil, err 77 }
+1 -1
appview/db/timeline.go
··· 183 func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 184 filters := make([]orm.Filter, 0) 185 if userIsFollowing != nil { 186 - filters = append(filters, orm.FilterIn("user_did", userIsFollowing)) 187 } 188 189 follows, err := GetFollows(e, limit, filters...)
··· 183 func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 184 filters := make([]orm.Filter, 0) 185 if userIsFollowing != nil { 186 + filters = append(filters, orm.FilterIn("did", userIsFollowing)) 187 } 188 189 follows, err := GetFollows(e, limit, filters...)

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
appview/db: more flexible tables
2/3 failed, 1/3 success
expand
merge conflicts detected
expand
  • appview/pages/markup/markdown.go:47
  • appview/pages/pages.go:57
expand 0 comments
1 commit
expand
appview/db: more flexible tables
3/3 success
expand
expand 0 comments