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 }