forked from tangled.org/core
this repo has no description
at master 6.4 kB view raw
1package db 2 3import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "tangled.org/core/appview/models" 10) 11 12func AddFollow(e Execer, follow *models.Follow) error { 13 query := `insert or ignore into follows (user_did, subject_did, rkey) values (?, ?, ?)` 14 _, err := e.Exec(query, follow.UserDid, follow.SubjectDid, follow.Rkey) 15 return err 16} 17 18// Get a follow record 19func GetFollow(e Execer, userDid, subjectDid string) (*models.Follow, error) { 20 query := `select user_did, subject_did, followed_at, rkey from follows where user_did = ? and subject_did = ?` 21 row := e.QueryRow(query, userDid, subjectDid) 22 23 var follow models.Follow 24 var followedAt string 25 err := row.Scan(&follow.UserDid, &follow.SubjectDid, &followedAt, &follow.Rkey) 26 if err != nil { 27 return nil, err 28 } 29 30 followedAtTime, err := time.Parse(time.RFC3339, followedAt) 31 if err != nil { 32 log.Println("unable to determine followed at time") 33 follow.FollowedAt = time.Now() 34 } else { 35 follow.FollowedAt = followedAtTime 36 } 37 38 return &follow, nil 39} 40 41// Remove a follow 42func DeleteFollow(e Execer, userDid, subjectDid string) error { 43 _, err := e.Exec(`delete from follows where user_did = ? and subject_did = ?`, userDid, subjectDid) 44 return err 45} 46 47// Remove a follow 48func DeleteFollowByRkey(e Execer, userDid, rkey string) error { 49 _, err := e.Exec(`delete from follows where user_did = ? and rkey = ?`, userDid, rkey) 50 return err 51} 52 53func GetFollowerFollowingCount(e Execer, did string) (models.FollowStats, error) { 54 var followers, following int64 55 err := e.QueryRow( 56 `SELECT 57 COUNT(CASE WHEN subject_did = ? THEN 1 END) AS followers, 58 COUNT(CASE WHEN user_did = ? THEN 1 END) AS following 59 FROM follows;`, did, did).Scan(&followers, &following) 60 if err != nil { 61 return models.FollowStats{}, err 62 } 63 return models.FollowStats{ 64 Followers: followers, 65 Following: following, 66 }, nil 67} 68 69func GetFollowerFollowingCounts(e Execer, dids []string) (map[string]models.FollowStats, error) { 70 if len(dids) == 0 { 71 return nil, nil 72 } 73 74 placeholders := make([]string, len(dids)) 75 for i := range placeholders { 76 placeholders[i] = "?" 77 } 78 placeholderStr := strings.Join(placeholders, ",") 79 80 args := make([]any, len(dids)*2) 81 for i, did := range dids { 82 args[i] = did 83 args[i+len(dids)] = did 84 } 85 86 query := fmt.Sprintf(` 87 select 88 coalesce(f.did, g.did) as did, 89 coalesce(f.followers, 0) as followers, 90 coalesce(g.following, 0) as following 91 from ( 92 select subject_did as did, count(*) as followers 93 from follows 94 where subject_did in (%s) 95 group by subject_did 96 ) f 97 full outer join ( 98 select user_did as did, count(*) as following 99 from follows 100 where user_did in (%s) 101 group by user_did 102 ) g on f.did = g.did`, 103 placeholderStr, placeholderStr) 104 105 result := make(map[string]models.FollowStats) 106 107 rows, err := e.Query(query, args...) 108 if err != nil { 109 return nil, err 110 } 111 defer rows.Close() 112 113 for rows.Next() { 114 var did string 115 var followers, following int64 116 if err := rows.Scan(&did, &followers, &following); err != nil { 117 return nil, err 118 } 119 result[did] = models.FollowStats{ 120 Followers: followers, 121 Following: following, 122 } 123 } 124 125 for _, did := range dids { 126 if _, exists := result[did]; !exists { 127 result[did] = models.FollowStats{ 128 Followers: 0, 129 Following: 0, 130 } 131 } 132 } 133 134 return result, nil 135} 136 137func GetFollows(e Execer, limit int, filters ...filter) ([]models.Follow, error) { 138 var follows []models.Follow 139 140 var conditions []string 141 var args []any 142 for _, filter := range filters { 143 conditions = append(conditions, filter.Condition()) 144 args = append(args, filter.Arg()...) 145 } 146 147 whereClause := "" 148 if conditions != nil { 149 whereClause = " where " + strings.Join(conditions, " and ") 150 } 151 limitClause := "" 152 if limit > 0 { 153 limitClause = " limit ?" 154 args = append(args, limit) 155 } 156 157 query := fmt.Sprintf( 158 `select user_did, subject_did, followed_at, rkey 159 from follows 160 %s 161 order by followed_at desc 162 %s 163 `, whereClause, limitClause) 164 165 rows, err := e.Query(query, args...) 166 if err != nil { 167 return nil, err 168 } 169 for rows.Next() { 170 var follow models.Follow 171 var followedAt string 172 err := rows.Scan( 173 &follow.UserDid, 174 &follow.SubjectDid, 175 &followedAt, 176 &follow.Rkey, 177 ) 178 if err != nil { 179 return nil, err 180 } 181 followedAtTime, err := time.Parse(time.RFC3339, followedAt) 182 if err != nil { 183 log.Println("unable to determine followed at time") 184 follow.FollowedAt = time.Now() 185 } else { 186 follow.FollowedAt = followedAtTime 187 } 188 follows = append(follows, follow) 189 } 190 return follows, nil 191} 192 193func GetFollowers(e Execer, did string) ([]models.Follow, error) { 194 return GetFollows(e, 0, FilterEq("subject_did", did)) 195} 196 197func GetFollowing(e Execer, did string) ([]models.Follow, error) { 198 return GetFollows(e, 0, FilterEq("user_did", did)) 199} 200 201func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) { 202 if len(subjectDids) == 0 || userDid == "" { 203 return make(map[string]models.FollowStatus), nil 204 } 205 206 result := make(map[string]models.FollowStatus) 207 208 for _, subjectDid := range subjectDids { 209 if userDid == subjectDid { 210 result[subjectDid] = models.IsSelf 211 } else { 212 result[subjectDid] = models.IsNotFollowing 213 } 214 } 215 216 var querySubjects []string 217 for _, subjectDid := range subjectDids { 218 if userDid != subjectDid { 219 querySubjects = append(querySubjects, subjectDid) 220 } 221 } 222 223 if len(querySubjects) == 0 { 224 return result, nil 225 } 226 227 placeholders := make([]string, len(querySubjects)) 228 args := make([]any, len(querySubjects)+1) 229 args[0] = userDid 230 231 for i, subjectDid := range querySubjects { 232 placeholders[i] = "?" 233 args[i+1] = subjectDid 234 } 235 236 query := fmt.Sprintf(` 237 SELECT subject_did 238 FROM follows 239 WHERE user_did = ? AND subject_did IN (%s) 240 `, strings.Join(placeholders, ",")) 241 242 rows, err := e.Query(query, args...) 243 if err != nil { 244 return nil, err 245 } 246 defer rows.Close() 247 248 for rows.Next() { 249 var subjectDid string 250 if err := rows.Scan(&subjectDid); err != nil { 251 return nil, err 252 } 253 result[subjectDid] = models.IsFollowing 254 } 255 256 return result, nil 257} 258 259func GetFollowStatus(e Execer, userDid, subjectDid string) models.FollowStatus { 260 statuses, err := getFollowStatuses(e, userDid, []string{subjectDid}) 261 if err != nil { 262 return models.IsNotFollowing 263 } 264 return statuses[subjectDid] 265} 266 267func GetFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) { 268 return getFollowStatuses(e, userDid, subjectDids) 269}