From ac8a096d5a7716cd88e4e1914ec37af2a200b743 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