website/pkg/models/forum_stats.go

158 lines
3.4 KiB
Go

package models
import "git.kirsle.net/apps/gosocial/pkg/log"
// ForumStatistics queries for forum-level statistics.
type ForumStatistics struct {
RecentThread *Thread
Threads uint64
Posts uint64
Users uint64
}
type ForumStatsMap map[uint64]*ForumStatistics
// MapForumStatistics looks up statistics for a set of forums.
func MapForumStatistics(forums []*Forum) ForumStatsMap {
var (
result = ForumStatsMap{}
IDs = []uint64{}
)
// Collect forum IDs and initialize the map.
for _, forum := range forums {
IDs = append(IDs, forum.ID)
result[forum.ID] = &ForumStatistics{}
}
// FIRST: count the threads in each forum.
{
// Hold the result of the count/group by query.
type group struct {
ID uint64
Threads uint64
}
var groups = []group{}
// Count comments grouped by thread IDs.
err := DB.Table(
"threads",
).Select(
"forum_id AS id, count(id) AS threads",
).Where(
"forum_id IN ?",
IDs,
).Group("forum_id").Scan(&groups)
if err.Error != nil {
log.Error("MapForumStatistics: SQL error: %s", err.Error)
}
// Map the results in.
for _, row := range groups {
log.Error("Got row: %+v", row)
if stats, ok := result[row.ID]; ok {
stats.Threads = row.Threads
}
}
}
// THEN: count all posts in all those threads.
{
type group struct {
ID uint64
Posts uint64
}
var groups = []group{}
err := DB.Table(
"comments",
).Joins(
"JOIN threads ON (table_name = 'threads' AND table_id = threads.id)",
).Joins(
"JOIN forums ON (threads.forum_id = forums.id)",
).Select(
"forums.id AS id, count(comments.id) AS posts",
).Where(
`table_name = 'threads' AND EXISTS (
SELECT 1
FROM threads
WHERE table_id = threads.id
AND threads.forum_id IN ?
)`,
IDs,
).Group("forums.id").Scan(&groups)
if err.Error != nil {
log.Error("SQL error collecting posts for forum: %s", err.Error)
}
// Map the results in.
for _, row := range groups {
log.Error("Got row2: %+v", row)
if stats, ok := result[row.ID]; ok {
stats.Posts = row.Posts
}
}
}
// THEN: count all distinct users in those threads.
// TODO: hairy
// {
// type group struct {
// ID uint64
// Users uint64
// }
// var groups = []group{}
// err := DB.Table(
// "comments",
// ).Joins(
// "JOIN threads ON (table_name = 'threads' AND table_id = threads.id)",
// ).Joins(
// "JOIN forums ON (threads.forum_id = forums.id)",
// ).Select(
// "forums.id AS forum_id, count(distinct(comments.user_id)) AS users",
// ).Where(
// // `table_name = 'threads' AND EXISTS (
// // SELECT 1
// // FROM threads
// // WHERE table_id = threads.id
// // AND threads.forum_id IN ?
// // )`,
// "forums.id IN ?",
// IDs,
// ).Group("forums.id").Scan(&groups)
// if err.Error != nil {
// log.Error("SQL error collecting users for forum: %s", err.Error)
// }
// // Map the results in.
// for _, row := range groups {
// log.Error("Got row2: %+v", row)
// if stats, ok := result[row.ID]; ok {
// stats.Users = row.Users
// }
// }
// }
// Get THE most recent thread on this forum.
return result
}
// Has stats for this thread? (we should..)
func (ts ForumStatsMap) Has(threadID uint64) bool {
_, ok := ts[threadID]
return ok
}
// Get thread stats.
func (ts ForumStatsMap) Get(threadID uint64) *ForumStatistics {
if stats, ok := ts[threadID]; ok {
return stats
}
return nil
}