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

Submissions

sign up or login to add to the discussion
boltless.me submitted #0
1 commit
expand
appview/db: more flexible tables
3/3 success
expand
no conflicts, ready to merge