db.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package dao
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "go-common/app/job/main/reply-feed/model"
  7. "go-common/library/database/sql"
  8. "go-common/library/log"
  9. xtime "go-common/library/time"
  10. "go-common/library/xstr"
  11. )
  12. const (
  13. _chunkedSize = 200
  14. _getReplyStatsByID = "SELECT id, `like`, hate, rcount, ctime FROM reply_%d WHERE id IN (%s)"
  15. _getReplyStats = "SELECT id, `like`, hate, rcount, ctime FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) ORDER BY `like` DESC LIMIT 2000"
  16. _getReplyReport = "SELECT rpid, count FROM reply_report_%d WHERE rpid IN (%s)"
  17. _getSubjectStat = "SELECT ctime from reply_subject_%d where oid=? and type=?"
  18. _getRpID = "SELECT id FROM reply_%d WHERE oid=? AND type=? AND root=0 AND state in (0,1,2,5,6) ORDER BY `like` DESC LIMIT 2000"
  19. _getSlotStats = "SELECT slot, name, algorithm, weight FROM reply_abtest_strategy"
  20. _getSlotsMapping = "SELECT name, slot FROM reply_abtest_strategy"
  21. _upsertStatisticsT = "INSERT INTO reply_abtest_statistics (%s) VALUES (%s) ON DUPLICATE KEY UPDATE %s"
  22. )
  23. var (
  24. _upsertStatistics = genSQL()
  25. )
  26. func genSQL() string {
  27. var (
  28. slot1 []string
  29. slot2 []string
  30. slot3 []string
  31. )
  32. slot1 = append(slot1, model.StatisticsDatabaseI...)
  33. slot1 = append(slot1, model.StatisticsDatabaseU...)
  34. slot1 = append(slot1, model.StatisticsDatabaseS...)
  35. for range model.StatisticsDatabaseI {
  36. slot2 = append(slot2, "?")
  37. }
  38. for _, c := range model.StatisticsDatabaseU {
  39. slot2 = append(slot2, "?")
  40. slot3 = append(slot3, c+"="+c+"+?")
  41. }
  42. for _, c := range model.StatisticsDatabaseS {
  43. slot2 = append(slot2, "?")
  44. slot3 = append(slot3, c+"="+"?")
  45. }
  46. return fmt.Sprintf(_upsertStatisticsT, strings.Join(slot1, ","), strings.Join(slot2, ","), strings.Join(slot3, ","))
  47. }
  48. func reportHit(oid int64) int64 {
  49. return oid % 200
  50. }
  51. func replyHit(oid int64) int64 {
  52. return oid % 200
  53. }
  54. func subjectHit(oid int64) int64 {
  55. return oid % 50
  56. }
  57. func splitReplyScore(buf []*model.ReplyScore, limit int) [][]*model.ReplyScore {
  58. var chunk []*model.ReplyScore
  59. chunks := make([][]*model.ReplyScore, 0, len(buf)/limit+1)
  60. for len(buf) >= limit {
  61. chunk, buf = buf[:limit], buf[limit:]
  62. chunks = append(chunks, chunk)
  63. }
  64. if len(buf) > 0 {
  65. chunks = append(chunks, buf)
  66. }
  67. return chunks
  68. }
  69. func splitString(buf []string, limit int) [][]string {
  70. var chunk []string
  71. chunks := make([][]string, 0, len(buf)/limit+1)
  72. for len(buf) >= limit {
  73. chunk, buf = buf[:limit], buf[limit:]
  74. chunks = append(chunks, chunk)
  75. }
  76. if len(buf) > 0 {
  77. chunks = append(chunks, buf)
  78. }
  79. return chunks
  80. }
  81. func split(buf []int64, limit int) [][]int64 {
  82. var chunk []int64
  83. chunks := make([][]int64, 0, len(buf)/limit+1)
  84. for len(buf) >= limit {
  85. chunk, buf = buf[:limit], buf[limit:]
  86. chunks = append(chunks, chunk)
  87. }
  88. if len(buf) > 0 {
  89. chunks = append(chunks, buf)
  90. }
  91. return chunks
  92. }
  93. // SlotStats get slot stat
  94. func (d *Dao) SlotStats(ctx context.Context) (ss []*model.SlotStat, err error) {
  95. rows, err := d.db.Query(ctx, _getSlotStats)
  96. if err != nil {
  97. log.Error("db.Query(%s) error(%v)", _getSlotStats, err)
  98. return
  99. }
  100. defer rows.Close()
  101. for rows.Next() {
  102. s := new(model.SlotStat)
  103. if err = rows.Scan(&s.Slot, &s.Name, &s.Algorithm, &s.Weight); err != nil {
  104. log.Error("rows.Scan() error(%v)", err)
  105. return
  106. }
  107. ss = append(ss, s)
  108. }
  109. if err = rows.Err(); err != nil {
  110. log.Error("rows.Err() error(%v)", err)
  111. }
  112. return
  113. }
  114. // RpIDs return rpIDs should in hot reply list.
  115. func (d *Dao) RpIDs(ctx context.Context, oid int64, tp int) (rpIDs []int64, err error) {
  116. query := fmt.Sprintf(_getRpID, replyHit(oid))
  117. rows, err := d.dbSlave.Query(ctx, query, oid, tp)
  118. if err != nil {
  119. log.Error("db.Query(%s) error(%v)", query, err)
  120. return
  121. }
  122. defer rows.Close()
  123. for rows.Next() {
  124. var ID int64
  125. if err = rows.Scan(&ID); err != nil {
  126. log.Error("rows.Scan() error(%v)", err)
  127. return
  128. }
  129. rpIDs = append(rpIDs, ID)
  130. }
  131. if err = rows.Err(); err != nil {
  132. log.Error("rows.Err() error(%v)", err)
  133. }
  134. return
  135. }
  136. // ReportStatsByID get report stats from database by ID
  137. func (d *Dao) ReportStatsByID(ctx context.Context, oid int64, rpIDs []int64) (reportMap map[int64]*model.ReplyStat, err error) {
  138. reportMap = make(map[int64]*model.ReplyStat)
  139. chunkedRpIDs := split(rpIDs, _chunkedSize)
  140. for _, ids := range chunkedRpIDs {
  141. var (
  142. query = fmt.Sprintf(_getReplyReport, reportHit(oid), xstr.JoinInts(ids))
  143. rows *sql.Rows
  144. )
  145. rows, err = d.dbSlave.Query(ctx, query)
  146. if err != nil {
  147. log.Error("db.Query(%s) error(%v)", query, err)
  148. return
  149. }
  150. for rows.Next() {
  151. var stat = new(model.ReplyStat)
  152. if err = rows.Scan(&stat.RpID, &stat.Report); err != nil {
  153. log.Error("rows.Scan() error(%v)", err)
  154. return
  155. }
  156. reportMap[stat.RpID] = stat
  157. }
  158. if err = rows.Err(); err != nil {
  159. rows.Close()
  160. log.Error("rows.Err() error(%v)", err)
  161. return
  162. }
  163. rows.Close()
  164. }
  165. return
  166. }
  167. // ReplyLHRCStatsByID return a reply like hate reply ctime stat by rpid.
  168. func (d *Dao) ReplyLHRCStatsByID(ctx context.Context, oid int64, rpIDs []int64) (replyMap map[int64]*model.ReplyStat, err error) {
  169. replyMap = make(map[int64]*model.ReplyStat)
  170. chunkedRpIDs := split(rpIDs, _chunkedSize)
  171. for _, ids := range chunkedRpIDs {
  172. var (
  173. query = fmt.Sprintf(_getReplyStatsByID, replyHit(oid), xstr.JoinInts(ids))
  174. rows *sql.Rows
  175. )
  176. rows, err = d.dbSlave.Query(ctx, query)
  177. if err != nil {
  178. log.Error("db.Query(%s) error(%v)", query, err)
  179. return
  180. }
  181. for rows.Next() {
  182. var (
  183. ctime xtime.Time
  184. stat = new(model.ReplyStat)
  185. )
  186. if err = rows.Scan(&stat.RpID, &stat.Like, &stat.Hate, &stat.Reply, &ctime); err != nil {
  187. log.Error("rows.Scan() error(%v)", err)
  188. return
  189. }
  190. stat.ReplyTime = ctime
  191. replyMap[stat.RpID] = stat
  192. }
  193. if err = rows.Err(); err != nil {
  194. rows.Close()
  195. log.Error("rows.Err() error(%v)", err)
  196. return
  197. }
  198. rows.Close()
  199. }
  200. return
  201. }
  202. // SubjectStats get subject ctime from database
  203. func (d *Dao) SubjectStats(ctx context.Context, oid int64, tp int) (ctime xtime.Time, err error) {
  204. query := fmt.Sprintf(_getSubjectStat, subjectHit(oid))
  205. if err = d.dbSlave.QueryRow(ctx, query, oid, tp).Scan(&ctime); err != nil {
  206. log.Error("db.QueryRow(%s) args(%d, %d) error(%v)", query, oid, tp, err)
  207. return
  208. }
  209. return
  210. }
  211. // ReplyLHRCStats get reply like, hate, reply, ctime stat from database, only get root reply which like>3, call it when back to source.
  212. func (d *Dao) ReplyLHRCStats(ctx context.Context, oid int64, tp int) (replyMap map[int64]*model.ReplyStat, err error) {
  213. replyMap = make(map[int64]*model.ReplyStat)
  214. query := fmt.Sprintf(_getReplyStats, replyHit(oid))
  215. rows, err := d.dbSlave.Query(ctx, query, oid, tp)
  216. if err != nil {
  217. log.Error("db.Query(%s) args(%d, %d) error(%v)", query, oid, tp, err)
  218. return
  219. }
  220. defer rows.Close()
  221. for rows.Next() {
  222. var (
  223. ctime xtime.Time
  224. stat = new(model.ReplyStat)
  225. )
  226. if err = rows.Scan(&stat.RpID, &stat.Like, &stat.Hate, &stat.Reply, &ctime); err != nil {
  227. log.Error("rows.Scan() error(%v)", err)
  228. return
  229. }
  230. stat.ReplyTime = ctime
  231. replyMap[stat.RpID] = stat
  232. }
  233. if err = rows.Err(); err != nil {
  234. log.Error("rows.Err() error(%v)", err)
  235. }
  236. return
  237. }
  238. // SlotsMapping get slots and name mapping.
  239. func (d *Dao) SlotsMapping(ctx context.Context) (slotsMap map[string]*model.SlotsMapping, err error) {
  240. slotsMap = make(map[string]*model.SlotsMapping)
  241. rows, err := d.db.Query(ctx, _getSlotsMapping)
  242. if err != nil {
  243. log.Error("db.Query(%s) args(%s) error(%v)", _getSlotsMapping, err)
  244. return
  245. }
  246. defer rows.Close()
  247. for rows.Next() {
  248. var (
  249. name string
  250. slot int
  251. )
  252. if err = rows.Scan(&name, &slot); err != nil {
  253. log.Error("rows.Scan error(%v)", err)
  254. return
  255. }
  256. slotsMapping, ok := slotsMap[name]
  257. if ok {
  258. slotsMapping.Slots = append(slotsMapping.Slots, slot)
  259. } else {
  260. slotsMapping = &model.SlotsMapping{
  261. Name: name,
  262. Slots: []int{slot},
  263. }
  264. }
  265. slotsMap[name] = slotsMapping
  266. }
  267. if err = rows.Err(); err != nil {
  268. log.Error("rows.Err() error(%v)", err)
  269. }
  270. return
  271. }
  272. // UpsertStatistics insert or update statistics into database
  273. func (d *Dao) UpsertStatistics(ctx context.Context, name string, date, hour int, s *model.StatisticsStat) (err error) {
  274. if _, err = d.db.Exec(ctx, _upsertStatistics,
  275. name, date, hour,
  276. s.HotLike, s.HotHate, s.HotReport, s.HotChildReply, s.TotalLike, s.TotalHate, s.TotalReport, s.TotalRootReply, s.TotalChildReply,
  277. s.HotLikeUV, s.HotHateUV, s.HotReportUV, s.HotChildUV, s.TotalLikeUV, s.TotalHateUV, s.TotalReportUV, s.TotalChildUV, s.TotalRootUV,
  278. s.HotLike, s.HotHate, s.HotReport, s.HotChildReply, s.TotalLike, s.TotalHate, s.TotalReport, s.TotalRootReply, s.TotalChildReply,
  279. s.HotLikeUV, s.HotHateUV, s.HotReportUV, s.HotChildUV, s.TotalLikeUV, s.TotalHateUV, s.TotalReportUV, s.TotalChildUV, s.TotalRootUV,
  280. ); err != nil {
  281. log.Error("upsert statistics failed. error(%v)", err)
  282. return
  283. }
  284. return
  285. }