From 67a7aa29f93d82ccd9aaadbfb5de878306d8b88d Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Sat, 18 Jul 2026 17:21:03 +0900 Subject: [PATCH] appview: remove `Rkey` from `models.Follow` From model layer, we expect follows to be unique per (did, subject). But from PDS, there is no such constraint. So hide the rkey from model while keeping duplicated records in DB. We keep duplicated records in DB to delete them all at once on unfollow Signed-off-by: Seongmin Lee --- appview/db/follow.go | 8 ++++---- appview/ingester.go | 3 +-- appview/models/follow.go | 1 - appview/state/follow.go | 6 +++--- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/appview/db/follow.go b/appview/db/follow.go index 758d5250..439bb1af 100644 --- a/appview/db/follow.go +++ b/appview/db/follow.go @@ -11,7 +11,7 @@ import ( "tangled.org/core/orm" ) -func UpsertFollow(e Execer, follow models.Follow) error { +func UpsertFollow(e Execer, rkey string, follow models.Follow) error { _, err := e.Exec( `insert into follows (did, rkey, subject_did, created) values (?, ?, ?, ?) @@ -19,7 +19,7 @@ func UpsertFollow(e Execer, follow models.Follow) error { subject_did = excluded.subject_did, created = excluded.created`, follow.UserDid, - follow.Rkey, + rkey, follow.SubjectDid, follow.FollowedAt.Format(time.RFC3339), ) @@ -189,9 +189,10 @@ func GetFollows(e Execer, limit int, filters ...orm.Filter) ([]models.Follow, er } query := fmt.Sprintf( - `select did, subject_did, created, rkey + `select did, subject_did, max(created) from follows %s + group by did, subject_did order by created desc %s `, whereClause, limitClause) @@ -209,7 +210,6 @@ func GetFollows(e Execer, limit int, filters ...orm.Filter) ([]models.Follow, er &follow.UserDid, &follow.SubjectDid, &followedAt, - &follow.Rkey, ) if err != nil { return nil, err diff --git a/appview/ingester.go b/appview/ingester.go index e6bdea74..e23e26a0 100644 --- a/appview/ingester.go +++ b/appview/ingester.go @@ -287,10 +287,9 @@ func (i *Ingester) ingestFollow(e *jmodels.Event, l *slog.Logger) error { return err } - err = db.UpsertFollow(i.Db, models.Follow{ + err = db.UpsertFollow(i.Db, e.Commit.RKey, models.Follow{ UserDid: did, SubjectDid: record.Subject, - Rkey: e.Commit.RKey, FollowedAt: followedAt, }) case jmodels.CommitOperationDelete: diff --git a/appview/models/follow.go b/appview/models/follow.go index d371226a..a3ef5efd 100644 --- a/appview/models/follow.go +++ b/appview/models/follow.go @@ -10,7 +10,6 @@ type Follow struct { UserDid string SubjectDid string FollowedAt time.Time - Rkey string } func (f *Follow) AsRecord() tangled.GraphFollow { diff --git a/appview/state/follow.go b/appview/state/follow.go index 341ce5f0..c7f721a5 100644 --- a/appview/state/follow.go +++ b/appview/state/follow.go @@ -46,9 +46,9 @@ func (s *State) Follow(w http.ResponseWriter, r *http.Request) { follow := models.Follow{ UserDid: currentUser.Did, SubjectDid: subjectIdent.DID.String(), - Rkey: tid.TID(), FollowedAt: time.Now(), } + rkey := tid.TID() tx, err := s.db.BeginTx(r.Context(), nil) if err != nil { @@ -57,7 +57,7 @@ func (s *State) Follow(w http.ResponseWriter, r *http.Request) { } defer tx.Rollback() - if err := db.UpsertFollow(tx, follow); err != nil { + if err := db.UpsertFollow(tx, rkey, follow); err != nil { s.logger.Error("failed to follow", "err", err) return } @@ -66,7 +66,7 @@ func (s *State) Follow(w http.ResponseWriter, r *http.Request) { resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ Collection: tangled.GraphFollowNSID, Repo: currentUser.Did, - Rkey: follow.Rkey, + Rkey: rkey, Record: &lexutil.LexiconTypeDecoder{ Val: &record, }, -- 2.51.2