github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/recalc.go (about) 1 package common 2 3 import ( 4 "database/sql" 5 //"log" 6 "strconv" 7 8 qgen "github.com/Azareal/Gosora/query_gen" 9 ) 10 11 var Recalc RecalcInt 12 13 type RecalcInt interface { 14 Replies() (count int, err error) 15 Forums() (count int, err error) 16 Subscriptions() (count int, err error) 17 ActivityStream() (count int, err error) 18 Users() error 19 Attachments() (count int, err error) 20 } 21 22 type DefaultRecalc struct { 23 getActivitySubscriptions *sql.Stmt 24 getActivityStream *sql.Stmt 25 getAttachments *sql.Stmt 26 getTopicCount *sql.Stmt 27 resetTopicCount *sql.Stmt 28 } 29 30 func NewDefaultRecalc(acc *qgen.Accumulator) (*DefaultRecalc, error) { 31 return &DefaultRecalc{ 32 getActivitySubscriptions: acc.Select("activity_subscriptions").Columns("targetID,targetType").Prepare(), 33 getActivityStream: acc.Select("activity_stream").Columns("asid,event,elementID,elementType,extra").Prepare(), 34 getAttachments: acc.Select("attachments").Columns("attachID,originID,originTable").Prepare(), 35 getTopicCount: acc.Count("topics").Where("parentID=?").Prepare(), 36 //resetTopicCount: acc.SimpleUpdateSelect("forums", "topicCount = tc", "topics", "count(*) as tc", "parentID=?", "", ""), 37 // TODO: Avoid using RawPrepare 38 resetTopicCount: acc.RawPrepare("UPDATE forums, (SELECT COUNT(*) as tc FROM topics WHERE parentID=?) AS src SET forums.topicCount=src.tc WHERE forums.fid=?"), 39 }, acc.FirstError() 40 } 41 42 func (s *DefaultRecalc) Replies() (count int, err error) { 43 var ltid int 44 err = Rstore.Each(func(r *Reply) error { 45 if ltid == r.ParentID && r.ParentID > 0 { 46 //return nil 47 } 48 if !Topics.Exists(r.ParentID) { 49 // TODO: Delete in chunks not one at a time? 50 if err := r.Delete(); err != nil { 51 return err 52 } 53 count++ 54 } 55 return nil 56 }) 57 return count, err 58 } 59 60 func (s *DefaultRecalc) Forums() (count int, err error) { 61 err = Forums.Each(func(f *Forum) error { 62 _, err := s.resetTopicCount.Exec(f.ID, f.ID) 63 if err != nil { 64 return err 65 } 66 count++ 67 return nil 68 }) 69 return count, err 70 } 71 72 func (s *DefaultRecalc) Subscriptions() (count int, err error) { 73 err = eachall(s.getActivitySubscriptions, func(r *sql.Rows) error { 74 var targetID int 75 var targetType string 76 err := r.Scan(&targetID, &targetType) 77 if err != nil { 78 return err 79 } 80 if targetType == "topic" { 81 if !Topics.Exists(targetID) { 82 // TODO: Delete in chunks not one at a time? 83 err := Subscriptions.DeleteResource(targetID, targetType) 84 if err != nil { 85 return err 86 } 87 count++ 88 } 89 } 90 return nil 91 }) 92 return count, err 93 } 94 95 type Existable interface { 96 Exists(id int) bool 97 } 98 99 func (s *DefaultRecalc) ActivityStream() (count int, err error) { 100 err = eachall(s.getActivityStream, func(r *sql.Rows) error { 101 var asid, elementID int 102 var event, elementType, extra string 103 err := r.Scan(&asid, &event, &elementID, &elementType, &extra) 104 if err != nil { 105 return err 106 } 107 //log.Print("asid:",asid) 108 var s Existable 109 switch elementType { 110 case "user": 111 if event == "reply" { 112 extraI, _ := strconv.Atoi(extra) 113 if extraI > 0 { 114 s = Prstore 115 elementID = extraI 116 } else { 117 return nil 118 } 119 } else { 120 return nil 121 } 122 case "topic": 123 s = Topics 124 // TODO: Delete reply events with an empty extra field 125 if event == "reply" { 126 extraI, _ := strconv.Atoi(extra) 127 if extraI > 0 { 128 s = Rstore 129 elementID = extraI 130 } 131 } 132 case "post": 133 s = Rstore 134 // TODO: Add a TopicExistsByReplyID for efficiency 135 /*_, err = TopicByReplyID(elementID) 136 if err == sql.ErrNoRows { 137 // TODO: Delete in chunks not one at a time? 138 err := Activity.Delete(asid) 139 if err != nil { 140 return err 141 } 142 count++ 143 } else if err != nil { 144 return err 145 }*/ 146 default: 147 return nil 148 } 149 if !s.Exists(elementID) { 150 // TODO: Delete in chunks not one at a time? 151 err := Activity.Delete(asid) 152 if err != nil { 153 return err 154 } 155 count++ 156 } 157 return nil 158 }) 159 return count, err 160 } 161 162 func (s *DefaultRecalc) Users() error { 163 return Users.Each(func(u *User) error { 164 return u.RecalcPostStats() 165 }) 166 } 167 168 func (s *DefaultRecalc) Attachments() (count int, err error) { 169 err = eachall(s.getAttachments, func(r *sql.Rows) error { 170 var aid, originID int 171 var originType string 172 err := r.Scan(&aid, &originID, &originType) 173 if err != nil { 174 return err 175 } 176 var s Existable 177 switch originType { 178 case "topics": 179 s = Topics 180 case "replies": 181 s = Rstore 182 default: 183 return nil 184 } 185 if !s.Exists(originID) { 186 // TODO: Delete in chunks not one at a time? 187 err := Attachments.Delete(aid) 188 if err != nil { 189 return err 190 } 191 count++ 192 } 193 return nil 194 }) 195 return count, err 196 }