working on weekly views

fix forum topic list not being updated on new topic
This commit is contained in:
Azareal 2020-12-18 08:54:44 +10:00
parent ed201f1898
commit f27d4a4c33
1 changed files with 117 additions and 11 deletions

View File

@ -1,10 +1,10 @@
package common package common
import ( import (
//"log"
"database/sql" "database/sql"
"strconv" "strconv"
"sync" "sync"
"time"
qgen "github.com/Azareal/Gosora/query_gen" qgen "github.com/Azareal/Gosora/query_gen"
) )
@ -14,6 +14,7 @@ var TopicList TopicListInt
const ( const (
TopicListDefault = iota TopicListDefault = iota
TopicListMostViewed TopicListMostViewed
TopicListWeekViews
) )
type TopicListHolder struct { type TopicListHolder struct {
@ -256,13 +257,28 @@ func (tList *DefaultTopicList) Tick() error {
} }
} }
// TODO: Avoid rebuilding the entire list on every tick
fList := make(map[int]*ForumTopicListHolder) fList := make(map[int]*ForumTopicListHolder)
for _, f := range fshort { for _, f := range fshort {
topicList, pagi, err := tList.GetListByForum(f, 1, 0) topicList, pagi := []*TopicsRow{}, Paginator{}
if f.TopicCount == 0 {
page := 1
_, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage)
pageList := Paginate(page, lastPage, 5)
pagi = Paginator{pageList, page, lastPage}
} else {
topicList, pagi, err = tList.getListByForum(f, 1, 0)
if err != nil { if err != nil {
return err return err
} }
}
fList[f.ID] = &ForumTopicListHolder{topicList, pagi} fList[f.ID] = &ForumTopicListHolder{topicList, pagi}
/*topicList, pagi, err := tList.GetListByForum(f, 1, 0)
if err != nil {
return err
}
fList[f.ID] = &ForumTopicListHolder{topicList, pagi}*/
} }
tList.forumLock.Lock() tList.forumLock.Lock()
@ -275,6 +291,48 @@ func (tList *DefaultTopicList) Tick() error {
return nil return nil
} }
/*var reloadForumMutex sync.Mutex
// TODO: Avoid firing this multiple times per sec tick
// TODO: Shard the forum topic list map
func (tList *DefaultTopicList) ReloadForum(id int) error {
reloadForumMutex.Lock()
defer reloadForumMutex.Unlock()
forum, err := Forums.Get(id)
if err != nil {
return err
}
ofList := make(map[int]*ForumTopicListHolder)
fList := make(map[int]*ForumTopicListHolder)
tList.forumLock.Lock()
ofList = tList.forums
for id, f := range ofList {
fList[id] = f
}
tList.forumLock.Unlock()
topicList, pagi := []*TopicsRow{}, Paginator{}
if forum.TopicCount == 0 {
page := 1
_, page, lastPage := PageOffset(forum.TopicCount, page, Config.ItemsPerPage)
pageList := Paginate(page, lastPage, 5)
pagi = Paginator{pageList, page, lastPage}
} else {
topicList, pagi, err = tList.getListByForum(forum, 1, 0)
if err != nil {
return err
}
}
fList[forum.ID] = &ForumTopicListHolder{topicList, pagi}
tList.forumLock.Lock()
tList.forums = fList
tList.forumLock.Unlock()
return nil
}*/
// TODO: Add Topics() method to *Forum? // TODO: Add Topics() method to *Forum?
// TODO: Implement orderby // TODO: Implement orderby
func (tList *DefaultTopicList) GetListByForum(f *Forum, page, orderby int) (topicList []*TopicsRow, pagi Paginator, err error) { func (tList *DefaultTopicList) GetListByForum(f *Forum, page, orderby int) (topicList []*TopicsRow, pagi Paginator, err error) {
@ -296,7 +354,10 @@ func (tList *DefaultTopicList) GetListByForum(f *Forum, page, orderby int) (topi
return h.List, h.Paginator, nil return h.List, h.Paginator, nil
} }
} }
return tList.getListByForum(f, page, orderby)
}
func (tList *DefaultTopicList) getListByForum(f *Forum, page, orderby int) (topicList []*TopicsRow, pagi Paginator, err error) {
// TODO: Does forum.TopicCount take the deleted items into consideration for guests? We don't have soft-delete yet, only hard-delete // TODO: Does forum.TopicCount take the deleted items into consideration for guests? We don't have soft-delete yet, only hard-delete
offset, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage) offset, page, lastPage := PageOffset(f.TopicCount, page, Config.ItemsPerPage)
@ -501,28 +562,56 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList []
} }
//log.Printf("argList: %+v\n",argList) //log.Printf("argList: %+v\n",argList)
//log.Printf("qlist: %+v\n",qlist) //log.Printf("qlist: %+v\n",qlist)
var orderq string var cols, orderq string
var stmt *sql.Stmt var stmt *sql.Stmt
if orderby == TopicListMostViewed { switch orderby {
case TopicListWeekViews:
tList.qLock.RLock()
stmt = tList.qcounts[len(argList)-2]
tList.qLock.RUnlock()
if stmt == nil {
orderq = "weekViews DESC,lastReplyAt DESC,createdBy DESC"
now := time.Now()
_, week := now.ISOWeek()
day := int(now.Weekday()) + 1
if week%2 == 0 { // is even?
cols = "tid,title,content,createdBy,is_closed,sticky,createdAt,lastReplyAt,lastReplyBy,lastReplyID,parentID,views,postCount,likeCount,attachCount,poll,data,(weekEvenViews+((weekOddViews/7)*" + strconv.Itoa(day) + ")) AS weekViews"
} else {
cols = "tid,title,content,createdBy,is_closed,sticky,createdAt,lastReplyAt,lastReplyBy,lastReplyID,parentID,views,postCount,likeCount,attachCount,poll,data,(weekOddViews+((weekEvenViews/7)*" + strconv.Itoa(day) + ")) AS weekViews"
}
topicCount, err = ArgQToWeekViewTopicCount(argList, qlist)
if err != nil {
return nil, Paginator{nil, 1, 1}, err
}
acc := qgen.NewAcc()
stmt = acc.Select("topics").Columns(cols).Where("parentID IN(" + qlist + ") AND (weekEvenViews!=0 OR weekOddViews!=0)").Orderby(orderq).Limit("?,?").ComplexPrepare()
if e := acc.FirstError(); e != nil {
return nil, Paginator{nil, 1, 1}, e
}
defer stmt.Close()
}
case TopicListMostViewed:
tList.qLock.RLock() tList.qLock.RLock()
stmt = tList.qcounts[len(argList)-2] stmt = tList.qcounts[len(argList)-2]
tList.qLock.RUnlock() tList.qLock.RUnlock()
if stmt == nil { if stmt == nil {
orderq = "views DESC,lastReplyAt DESC,createdBy DESC" orderq = "views DESC,lastReplyAt DESC,createdBy DESC"
cols = "tid,title,content,createdBy,is_closed,sticky,createdAt,lastReplyAt,lastReplyBy,lastReplyID,parentID,views,postCount,likeCount,attachCount,poll,data"
} }
} else { default:
tList.qLock2.RLock() tList.qLock2.RLock()
stmt = tList.qcounts2[len(argList)-2] stmt = tList.qcounts2[len(argList)-2]
tList.qLock2.RUnlock() tList.qLock2.RUnlock()
if stmt == nil { if stmt == nil {
orderq = "sticky DESC,lastReplyAt DESC,createdBy DESC" orderq = "sticky DESC,lastReplyAt DESC,createdBy DESC"
cols = "tid,title,content,createdBy,is_closed,sticky,createdAt,lastReplyAt,lastReplyBy,lastReplyID,parentID,views,postCount,likeCount,attachCount,poll,data"
} }
} }
offset, page, lastPage := PageOffset(topicCount, page, Config.ItemsPerPage) offset, page, lastPage := PageOffset(topicCount, page, Config.ItemsPerPage)
// TODO: Prepare common qlist lengths to speed this up in common cases, prepared statements are prepared lazily anyway, so it probably doesn't matter if we do ten or so // TODO: Prepare common qlist lengths to speed this up in common cases, prepared statements are prepared lazily anyway, so it probably doesn't matter if we do ten or so
if stmt == nil { if stmt == nil {
stmt, err = qgen.Builder.SimpleSelect("topics", "tid,title,content,createdBy,is_closed,sticky,createdAt,lastReplyAt,lastReplyBy,lastReplyID,parentID,views,postCount,likeCount,attachCount,poll,data", "parentID IN("+qlist+")", orderq, "?,?") stmt, err = qgen.Builder.SimpleSelect("topics", cols, "parentID IN("+qlist+")", orderq, "?,?")
if err != nil { if err != nil {
return nil, Paginator{nil, 1, 1}, err return nil, Paginator{nil, 1, 1}, err
} }
@ -538,18 +627,20 @@ func (tList *DefaultTopicList) getList(page, orderby, topicCount int, argList []
} }
defer rows.Close() defer rows.Close()
rcache := Rstore.GetCache() rc := Rstore.GetCache()
rcap := rcache.GetCapacity() rcap := rc.GetCapacity()
rlen := rcache.Length() rlen := rc.Length()
tc := Topics.GetCache() tc := Topics.GetCache()
reqUserList := make(map[int]bool) reqUserList := make(map[int]bool)
for rows.Next() { for rows.Next() {
// TODO: Embed Topic structs in TopicsRow to make it easier for us to reuse this work in the topic cache // TODO: Embed Topic structs in TopicsRow to make it easier for us to reuse this work in the topic cache
t := TopicsRow{} t := TopicsRow{}
err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IsClosed, &t.Sticky, &t.CreatedAt, &t.LastReplyAt, &t.LastReplyBy, &t.LastReplyID, &t.ParentID, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data) //var weekViews []uint8
err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IsClosed, &t.Sticky, &t.CreatedAt, &t.LastReplyAt, &t.LastReplyBy, &t.LastReplyID, &t.ParentID, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data /*, &weekViews*/)
if err != nil { if err != nil {
return nil, Paginator{nil, 1, 1}, err return nil, Paginator{nil, 1, 1}, err
} }
//log.Printf("weekViews: %+v\n", weekViews)
t.Link = BuildTopicURL(NameToSlug(t.Title), t.ID) t.Link = BuildTopicURL(NameToSlug(t.Title), t.ID)
// TODO: Pass forum to something like topicItem.Forum and use that instead of these two properties? Could be more flexible. // TODO: Pass forum to something like topicItem.Forum and use that instead of these two properties? Could be more flexible.
@ -674,6 +765,21 @@ func ArgQToTopicCount(argList []interface{}, qlist string) (topicCount int, err
return topicCount, err return topicCount, err
} }
// Internal. Don't rely on it.
func ArgQToWeekViewTopicCount(argList []interface{}, qlist string) (topicCount int, err error) {
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "parentID IN("+qlist+") AND (weekEvenViews!=0 OR weekOddViews!=0)", "")
if err != nil {
return 0, err
}
defer topicCountStmt.Close()
err = topicCountStmt.QueryRow(argList...).Scan(&topicCount)
if err != nil && err != ErrNoRows {
return 0, err
}
return topicCount, err
}
func TopicCountInForums(forums []Forum) (topicCount int, err error) { func TopicCountInForums(forums []Forum) (topicCount int, err error) {
for _, f := range forums { for _, f := range forums {
topicCount += f.TopicCount topicCount += f.TopicCount