diff --git a/cmd/nonshy/main.go b/cmd/nonshy/main.go index 3530e2c..b0a874d 100644 --- a/cmd/nonshy/main.go +++ b/cmd/nonshy/main.go @@ -261,6 +261,21 @@ func main() { return err } + return nil + }, + }, + { + Name: "photo-counts", + Usage: "repopulate cached Likes and Comment counts on photos", + Action: func(c *cli.Context) error { + initdb(c) + + log.Info("Running BackfillPhotoCounts()") + err := backfill.BackfillPhotoCounts() + if err != nil { + return err + } + return nil }, }, diff --git a/pkg/controller/api/likes.go b/pkg/controller/api/likes.go index dc97c1b..2748225 100644 --- a/pkg/controller/api/likes.go +++ b/pkg/controller/api/likes.go @@ -204,6 +204,13 @@ func Likes() http.HandlerFunc { } } + // Refresh cached like counts. + if req.TableName == "photos" { + if err := models.UpdatePhotoCachedCounts(tableID); err != nil { + log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err) + } + } + // Send success response. SendJSON(w, http.StatusOK, Response{ OK: true, diff --git a/pkg/controller/comment/post_comment.go b/pkg/controller/comment/post_comment.go index 537c293..2d0f6b4 100644 --- a/pkg/controller/comment/post_comment.go +++ b/pkg/controller/comment/post_comment.go @@ -121,6 +121,14 @@ func PostComment() http.HandlerFunc { // Log the change. models.LogDeleted(&models.User{ID: comment.UserID}, currentUser, "comments", comment.ID, "Deleted a comment.", comment) } + + // Refresh cached like counts. + if tableName == "photos" { + if err := models.UpdatePhotoCachedCounts(tableID); err != nil { + log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err) + } + } + templates.Redirect(w, fromURL) return } @@ -174,6 +182,13 @@ func PostComment() http.HandlerFunc { session.Flash(w, r, "Comment added!") templates.Redirect(w, fromURL) + // Refresh cached comment counts. + if tableName == "photos" { + if err := models.UpdatePhotoCachedCounts(tableID); err != nil { + log.Error("UpdatePhotoCachedCount(%d): %s", tableID, err) + } + } + // Log the change. models.LogCreated(currentUser, "comments", comment.ID, "Posted a new comment.\n\n---\n\n"+message) diff --git a/pkg/controller/photo/site_gallery.go b/pkg/controller/photo/site_gallery.go index a387afc..741ceeb 100644 --- a/pkg/controller/photo/site_gallery.go +++ b/pkg/controller/photo/site_gallery.go @@ -18,10 +18,8 @@ func SiteGallery() http.HandlerFunc { var sortWhitelist = []string{ "created_at desc", "created_at asc", - - // Custom (advanced) sort options. - "by_likes", - "by_comments", + "like_count desc", + "comment_count desc", } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/controller/photo/user_gallery.go b/pkg/controller/photo/user_gallery.go index b73786e..47004f0 100644 --- a/pkg/controller/photo/user_gallery.go +++ b/pkg/controller/photo/user_gallery.go @@ -19,10 +19,8 @@ func UserPhotos() http.HandlerFunc { "pinned desc nulls last, updated_at desc", "created_at desc", "created_at asc", - - // Custom (advanced) sort options. - "by_likes", - "by_comments", + "like_count desc", + "comment_count desc", } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/models/backfill/backfill_photo_counts.go b/pkg/models/backfill/backfill_photo_counts.go new file mode 100644 index 0000000..50b726e --- /dev/null +++ b/pkg/models/backfill/backfill_photo_counts.go @@ -0,0 +1,25 @@ +package backfill + +import ( + "code.nonshy.com/nonshy/website/pkg/models" +) + +// BackfillPhotoCounts recomputes the cached Likes and Comment counts on photos. +func BackfillPhotoCounts() error { + res := models.DB.Exec(` + UPDATE photos + SET like_count = ( + SELECT count(id) + FROM likes + WHERE table_name='photos' + AND table_id=photos.id + ), + comment_count = ( + SELECT count(id) + FROM comments + WHERE table_name='photos' + AND table_id=photos.id + ); + `) + return res.Error +} diff --git a/pkg/models/photo.go b/pkg/models/photo.go index 3fbeeb9..bc47358 100644 --- a/pkg/models/photo.go +++ b/pkg/models/photo.go @@ -25,6 +25,8 @@ type Photo struct { Gallery bool `gorm:"index"` // photo appears in the public gallery (if public) Explicit bool `gorm:"index"` // is an explicit photo Pinned bool `gorm:"index"` // user pins it to the front of their gallery + LikeCount int64 `gorm:"index"` // cache of 'likes' count + CommentCount int64 `gorm:"index"` // cache of comments count CreatedAt time.Time `gorm:"index"` UpdatedAt time.Time } @@ -135,24 +137,6 @@ func PaginateUserPhotos(userID uint64, conf UserGallery, pager *Pagination) ([]* placeholders = append(placeholders, explicit[0]) } - // Custom SORT parameters. - switch pager.Sort { - case "by_likes": - pager.Sort = `( - SELECT count(likes.id) - FROM likes - WHERE likes.table_name = 'photos' - AND likes.table_id = photos.id - ) DESC` - case "by_comments": - pager.Sort = `( - SELECT count(comments.id) - FROM comments - WHERE comments.table_name = 'photos' - AND comments.table_id = photos.id - ) DESC NULLS LAST` - } - query := DB.Where( strings.Join(wheres, " AND "), placeholders..., @@ -690,38 +674,33 @@ func PaginateGalleryPhotos(user *User, conf Gallery, pager *Pagination) ([]*Phot ) } - // Get count pre-sorting. - query.Model(&Photo{}).Count(&pager.Total) - - // Custom SORT parameters. - switch pager.Sort { - case "by_likes": - query = query.Select(` - photos.*, - COUNT(likes.id) AS like_count - `).Joins( - "LEFT OUTER JOIN likes ON (likes.table_name='photos' AND likes.table_id=photos.id)", - ).Where( - "likes.table_name = 'photos' AND likes.table_id = photos.id", - ).Group("photos.id") - pager.Sort = `like_count DESC` - case "by_comments": - query = query.Select(` - photos.*, - COUNT(comments.id) AS comment_count - `).Joins( - "LEFT OUTER JOIN comments ON (comments.table_name='photos' AND comments.table_id=photos.id)", - ).Where( - "comments.table_name = 'photos' AND comments.table_id = photos.id", - ).Group("photos.id") - pager.Sort = `comment_count DESC` - } - query = query.Order(pager.Sort) + query.Model(&Photo{}).Count(&pager.Total) result := query.Offset(pager.GetOffset()).Limit(pager.PerPage).Find(&p) return p, result.Error } +// UpdatePhotoCachedCounts will refresh the cached like/comment count on the photos table. +func UpdatePhotoCachedCounts(photoID uint64) error { + res := DB.Exec(` + UPDATE photos + SET like_count = ( + SELECT count(id) + FROM likes + WHERE table_name='photos' + AND table_id=photos.id + ), + comment_count = ( + SELECT count(id) + FROM comments + WHERE table_name='photos' + AND table_id=photos.id + ) + WHERE photos.id = ?; + `, photoID) + return res.Error +} + // Save photo. func (p *Photo) Save() error { result := DB.Save(p) diff --git a/web/templates/photo/gallery.html b/web/templates/photo/gallery.html index 708eb73..105599c 100644 --- a/web/templates/photo/gallery.html +++ b/web/templates/photo/gallery.html @@ -414,8 +414,8 @@ {{end}} - - + +