follows.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. // Copyright 2022 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package db
  5. import (
  6. "context"
  7. "github.com/pkg/errors"
  8. "gorm.io/gorm"
  9. )
  10. // FollowsStore is the persistent interface for user follows.
  11. //
  12. // NOTE: All methods are sorted in alphabetical order.
  13. type FollowsStore interface {
  14. // Follow marks the user to follow the other user.
  15. Follow(ctx context.Context, userID, followID int64) error
  16. // IsFollowing returns true if the user is following the other user.
  17. IsFollowing(ctx context.Context, userID, followID int64) bool
  18. // Unfollow removes the mark the user to follow the other user.
  19. Unfollow(ctx context.Context, userID, followID int64) error
  20. }
  21. var Follows FollowsStore
  22. var _ FollowsStore = (*follows)(nil)
  23. type follows struct {
  24. *gorm.DB
  25. }
  26. // NewFollowsStore returns a persistent interface for user follows with given
  27. // database connection.
  28. func NewFollowsStore(db *gorm.DB) FollowsStore {
  29. return &follows{DB: db}
  30. }
  31. func (*follows) updateFollowingCount(tx *gorm.DB, userID, followID int64) error {
  32. /*
  33. Equivalent SQL for PostgreSQL:
  34. UPDATE "user"
  35. SET num_followers = (
  36. SELECT COUNT(*) FROM follow WHERE follow_id = @followID
  37. )
  38. WHERE id = @followID
  39. */
  40. err := tx.Model(&User{}).
  41. Where("id = ?", followID).
  42. Update(
  43. "num_followers",
  44. tx.Model(&Follow{}).Select("COUNT(*)").Where("follow_id = ?", followID),
  45. ).
  46. Error
  47. if err != nil {
  48. return errors.Wrap(err, `update "user.num_followers"`)
  49. }
  50. /*
  51. Equivalent SQL for PostgreSQL:
  52. UPDATE "user"
  53. SET num_following = (
  54. SELECT COUNT(*) FROM follow WHERE user_id = @userID
  55. )
  56. WHERE id = @userID
  57. */
  58. err = tx.Model(&User{}).
  59. Where("id = ?", userID).
  60. Update(
  61. "num_following",
  62. tx.Model(&Follow{}).Select("COUNT(*)").Where("user_id = ?", userID),
  63. ).
  64. Error
  65. if err != nil {
  66. return errors.Wrap(err, `update "user.num_following"`)
  67. }
  68. return nil
  69. }
  70. func (db *follows) Follow(ctx context.Context, userID, followID int64) error {
  71. if userID == followID {
  72. return nil
  73. }
  74. return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
  75. f := &Follow{
  76. UserID: userID,
  77. FollowID: followID,
  78. }
  79. result := tx.FirstOrCreate(f, f)
  80. if result.Error != nil {
  81. return errors.Wrap(result.Error, "upsert")
  82. } else if result.RowsAffected <= 0 {
  83. return nil // Relation already exists
  84. }
  85. return db.updateFollowingCount(tx, userID, followID)
  86. })
  87. }
  88. func (db *follows) IsFollowing(ctx context.Context, userID, followID int64) bool {
  89. return db.WithContext(ctx).Where("user_id = ? AND follow_id = ?", userID, followID).First(&Follow{}).Error == nil
  90. }
  91. func (db *follows) Unfollow(ctx context.Context, userID, followID int64) error {
  92. if userID == followID {
  93. return nil
  94. }
  95. return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
  96. err := tx.Where("user_id = ? AND follow_id = ?", userID, followID).Delete(&Follow{}).Error
  97. if err != nil {
  98. return errors.Wrap(err, "delete")
  99. }
  100. return db.updateFollowingCount(tx, userID, followID)
  101. })
  102. }
  103. // Follow represents relations of users and their followers.
  104. type Follow struct {
  105. ID int64 `gorm:"primaryKey"`
  106. UserID int64 `xorm:"UNIQUE(follow)" gorm:"uniqueIndex:follow_user_follow_unique;not null"`
  107. FollowID int64 `xorm:"UNIQUE(follow)" gorm:"uniqueIndex:follow_user_follow_unique;not null"`
  108. }