From fb8b603ecf8928e810d0fff391ac2ecad17e336c Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Tue, 18 Nov 2025 00:17:22 +0900 Subject: [PATCH] appview/db: split star subjects Change-Id: rxozvmonryklxymyxrtqkmomnoryosxw renamed `starred_by_did` column to `did` remove foreign key constraints from `repo_at` column to support targetting non-ingested repository or `sh.tangled.string`. the column isn't renamed yet because I'm afraid to break somewhere with that rename. We really need some test code here. Signed-off-by: Seongmin Lee --- appview/db/db.go | 41 +++++++++++++++++++ appview/db/star.go | 34 +++++++-------- appview/db/timeline.go | 2 +- appview/ingester.go | 6 +-- appview/models/star.go | 8 ++-- appview/notify/db/db.go | 7 +++- appview/notify/posthog/notifier.go | 4 +- .../timeline/fragments/timeline.html | 2 +- appview/state/profile.go | 4 +- appview/state/star.go | 6 +-- 10 files changed, 80 insertions(+), 34 deletions(-) diff --git a/appview/db/db.go b/appview/db/db.go index 9dba07c7..f743a76e 100644 --- a/appview/db/db.go +++ b/appview/db/db.go @@ -1128,6 +1128,47 @@ func Make(ctx context.Context, dbPath string) (*DB, error) { return err }) + // remove the foreign key constraints from stars. + // using same column names for backwards compatibility, but now `repo_at` + // column can represent literally anything. + runMigration(conn, logger, "generalize-stars-subject", func(tx *sql.Tx) error { + _, err := tx.Exec(` + create table stars_new ( + id integer primary key autoincrement, + did text not null, + rkey text not null, + + repo_at text not null, + + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), + unique(did, rkey), + unique(did, repo_at) + ); + + insert into stars_new ( + id, + did, + rkey, + repo_at, + created + ) + select + id, + starred_by_did, + rkey, + repo_at, + created + from stars; + + drop table stars; + alter table stars_new rename to stars; + + create index if not exists idx_stars_created on stars(created); + create index if not exists idx_stars_repo_at_created on stars(repo_at, created); + `) + return err + }) + return &DB{ db, logger, diff --git a/appview/db/star.go b/appview/db/star.go index f7f7c59b..4d8c8e6b 100644 --- a/appview/db/star.go +++ b/appview/db/star.go @@ -14,10 +14,10 @@ import ( ) func AddStar(e Execer, star *models.Star) error { - query := `insert or ignore into stars (starred_by_did, repo_at, rkey) values (?, ?, ?)` + query := `insert or ignore into stars (did, repo_at, rkey) values (?, ?, ?)` _, err := e.Exec( query, - star.StarredByDid, + star.Did, star.RepoAt.String(), star.Rkey, ) @@ -25,16 +25,16 @@ func AddStar(e Execer, star *models.Star) error { } // Get a star record -func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*models.Star, error) { +func GetStar(e Execer, did string, repoAt syntax.ATURI) (*models.Star, error) { query := ` - select starred_by_did, repo_at, created, rkey + select did, repo_at, created, rkey from stars - where starred_by_did = ? and repo_at = ?` - row := e.QueryRow(query, starredByDid, repoAt) + where did = ? and repo_at = ?` + row := e.QueryRow(query, did, repoAt) var star models.Star var created string - err := row.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey) + err := row.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey) if err != nil { return nil, err } @@ -51,21 +51,21 @@ func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*models.Star, } // Remove a star -func DeleteStar(e Execer, starredByDid string, repoAt syntax.ATURI) error { - _, err := e.Exec(`delete from stars where starred_by_did = ? and repo_at = ?`, starredByDid, repoAt) +func DeleteStar(e Execer, did string, repoAt syntax.ATURI) error { + _, err := e.Exec(`delete from stars where did = ? and repo_at = ?`, did, repoAt) return err } // Remove a star -func DeleteStarByRkey(e Execer, starredByDid string, rkey string) error { - _, err := e.Exec(`delete from stars where starred_by_did = ? and rkey = ?`, starredByDid, rkey) +func DeleteStarByRkey(e Execer, did string, rkey string) error { + _, err := e.Exec(`delete from stars where did = ? and rkey = ?`, did, rkey) return err } func GetStarCount(e Execer, repoAt syntax.ATURI) (int, error) { stars := 0 err := e.QueryRow( - `select count(starred_by_did) from stars where repo_at = ?`, repoAt).Scan(&stars) + `select count(did) from stars where repo_at = ?`, repoAt).Scan(&stars) if err != nil { return 0, err } @@ -91,7 +91,7 @@ func getStarStatuses(e Execer, userDid string, repoAts []syntax.ATURI) (map[stri query := fmt.Sprintf(` SELECT repo_at FROM stars - WHERE starred_by_did = ? AND repo_at IN (%s) + WHERE did = ? AND repo_at IN (%s) `, strings.Join(placeholders, ",")) rows, err := e.Query(query, args...) @@ -149,7 +149,7 @@ func GetStars(e Execer, limit int, filters ...filter) ([]models.Star, error) { } repoQuery := fmt.Sprintf( - `select starred_by_did, repo_at, created, rkey + `select did, repo_at, created, rkey from stars %s order by created desc @@ -166,7 +166,7 @@ func GetStars(e Execer, limit int, filters ...filter) ([]models.Star, error) { for rows.Next() { var star models.Star var created string - err := rows.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey) + err := rows.Scan(&star.Did, &star.RepoAt, &created, &star.Rkey) if err != nil { return nil, err } @@ -252,7 +252,7 @@ func GetAllStars(e Execer, limit int) ([]models.Star, error) { rows, err := e.Query(` select - s.starred_by_did, + s.did, s.repo_at, s.rkey, s.created, @@ -276,7 +276,7 @@ func GetAllStars(e Execer, limit int) ([]models.Star, error) { var starCreatedAt, repoCreatedAt string if err := rows.Scan( - &star.StarredByDid, + &star.Did, &star.RepoAt, &star.Rkey, &starCreatedAt, diff --git a/appview/db/timeline.go b/appview/db/timeline.go index 46439c77..1b255d7d 100644 --- a/appview/db/timeline.go +++ b/appview/db/timeline.go @@ -146,7 +146,7 @@ func getTimelineRepos(e Execer, limit int, loggedInUserDid string, userIsFollowi func getTimelineStars(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { filters := make([]filter, 0) if userIsFollowing != nil { - filters = append(filters, FilterIn("starred_by_did", userIsFollowing)) + filters = append(filters, FilterIn("did", userIsFollowing)) } stars, err := GetStars(e, limit, filters...) diff --git a/appview/ingester.go b/appview/ingester.go index 1c5d6d0a..40c9e572 100644 --- a/appview/ingester.go +++ b/appview/ingester.go @@ -121,9 +121,9 @@ func (i *Ingester) ingestStar(e *jmodels.Event) error { return err } err = db.AddStar(i.Db, &models.Star{ - StarredByDid: did, - RepoAt: subjectUri, - Rkey: e.Commit.RKey, + Did: did, + RepoAt: subjectUri, + Rkey: e.Commit.RKey, }) case jmodels.CommitOperationDelete: err = db.DeleteStarByRkey(i.Db, did, e.Commit.RKey) diff --git a/appview/models/star.go b/appview/models/star.go index 8ba5806f..6fc36129 100644 --- a/appview/models/star.go +++ b/appview/models/star.go @@ -7,10 +7,10 @@ import ( ) type Star struct { - StarredByDid string - RepoAt syntax.ATURI - Created time.Time - Rkey string + Did string + RepoAt syntax.ATURI + Created time.Time + Rkey string // optionally, populate this when querying for reverse mappings Repo *Repo diff --git a/appview/notify/db/db.go b/appview/notify/db/db.go index 7ef9321a..0cd55892 100644 --- a/appview/notify/db/db.go +++ b/appview/notify/db/db.go @@ -7,6 +7,7 @@ import ( "slices" "github.com/bluesky-social/indigo/atproto/syntax" + "tangled.org/core/api/tangled" "tangled.org/core/appview/db" "tangled.org/core/appview/models" "tangled.org/core/appview/notify" @@ -36,6 +37,10 @@ func (n *databaseNotifier) NewRepo(ctx context.Context, repo *models.Repo) { } func (n *databaseNotifier) NewStar(ctx context.Context, star *models.Star) { + if star.RepoAt.Collection().String() != tangled.RepoNSID { + // skip string stars for now + return + } var err error repo, err := db.GetRepo(n.db, db.FilterEq("at_uri", string(star.RepoAt))) if err != nil { @@ -43,7 +48,7 @@ func (n *databaseNotifier) NewStar(ctx context.Context, star *models.Star) { return } - actorDid := syntax.DID(star.StarredByDid) + actorDid := syntax.DID(star.Did) recipients := []syntax.DID{syntax.DID(repo.Did)} eventType := models.NotificationTypeRepoStarred entityType := "repo" diff --git a/appview/notify/posthog/notifier.go b/appview/notify/posthog/notifier.go index 4ca57511..2679d946 100644 --- a/appview/notify/posthog/notifier.go +++ b/appview/notify/posthog/notifier.go @@ -37,7 +37,7 @@ func (n *posthogNotifier) NewRepo(ctx context.Context, repo *models.Repo) { func (n *posthogNotifier) NewStar(ctx context.Context, star *models.Star) { err := n.client.Enqueue(posthog.Capture{ - DistinctId: star.StarredByDid, + DistinctId: star.Did, Event: "star", Properties: posthog.Properties{"repo_at": star.RepoAt.String()}, }) @@ -48,7 +48,7 @@ func (n *posthogNotifier) NewStar(ctx context.Context, star *models.Star) { func (n *posthogNotifier) DeleteStar(ctx context.Context, star *models.Star) { err := n.client.Enqueue(posthog.Capture{ - DistinctId: star.StarredByDid, + DistinctId: star.Did, Event: "unstar", Properties: posthog.Properties{"repo_at": star.RepoAt.String()}, }) diff --git a/appview/pages/templates/timeline/fragments/timeline.html b/appview/pages/templates/timeline/fragments/timeline.html index 081e112a..2f262c5a 100644 --- a/appview/pages/templates/timeline/fragments/timeline.html +++ b/appview/pages/templates/timeline/fragments/timeline.html @@ -61,7 +61,7 @@ {{ $event := index . 1 }} {{ $star := $event.Star }} {{ with $star }} - {{ $starrerHandle := resolve .StarredByDid }} + {{ $starrerHandle := resolve .Did }} {{ $repoOwnerHandle := resolve .Repo.Did }}
{{ template "user/fragments/picHandleLink" $starrerHandle }} diff --git a/appview/state/profile.go b/appview/state/profile.go index 2c47add2..3e2cd19f 100644 --- a/appview/state/profile.go +++ b/appview/state/profile.go @@ -66,7 +66,7 @@ func (s *State) profile(r *http.Request) (*pages.ProfileCard, error) { return nil, fmt.Errorf("failed to get string count: %w", err) } - starredCount, err := db.CountStars(s.db, db.FilterEq("starred_by_did", did)) + starredCount, err := db.CountStars(s.db, db.FilterEq("did", did)) if err != nil { return nil, fmt.Errorf("failed to get starred repo count: %w", err) } @@ -211,7 +211,7 @@ func (s *State) starredPage(w http.ResponseWriter, r *http.Request) { } l = l.With("profileDid", profile.UserDid, "profileHandle", profile.UserHandle) - stars, err := db.GetStars(s.db, 0, db.FilterEq("starred_by_did", profile.UserDid)) + stars, err := db.GetStars(s.db, 0, db.FilterEq("did", profile.UserDid)) if err != nil { l.Error("failed to get stars", "err", err) s.pages.Error500(w) diff --git a/appview/state/star.go b/appview/state/star.go index 3473f65d..49c4e96d 100644 --- a/appview/state/star.go +++ b/appview/state/star.go @@ -57,9 +57,9 @@ func (s *State) Star(w http.ResponseWriter, r *http.Request) { log.Println("created atproto record: ", resp.Uri) star := &models.Star{ - StarredByDid: currentUser.Did, - RepoAt: subjectUri, - Rkey: rkey, + Did: currentUser.Did, + RepoAt: subjectUri, + Rkey: rkey, } err = db.AddStar(s.db, star) -- 2.43.0