You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

issue_comment_list.go 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. // Copyright 2018 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "context"
  7. "code.gitea.io/gitea/models/db"
  8. issues_model "code.gitea.io/gitea/models/issues"
  9. repo_model "code.gitea.io/gitea/models/repo"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/container"
  12. )
  13. // CommentList defines a list of comments
  14. type CommentList []*Comment
  15. func (comments CommentList) getPosterIDs() []int64 {
  16. posterIDs := make(map[int64]struct{}, len(comments))
  17. for _, comment := range comments {
  18. if _, ok := posterIDs[comment.PosterID]; !ok {
  19. posterIDs[comment.PosterID] = struct{}{}
  20. }
  21. }
  22. return container.KeysInt64(posterIDs)
  23. }
  24. func (comments CommentList) loadPosters(e db.Engine) error {
  25. if len(comments) == 0 {
  26. return nil
  27. }
  28. posterIDs := comments.getPosterIDs()
  29. posterMaps := make(map[int64]*user_model.User, len(posterIDs))
  30. left := len(posterIDs)
  31. for left > 0 {
  32. limit := defaultMaxInSize
  33. if left < limit {
  34. limit = left
  35. }
  36. err := e.
  37. In("id", posterIDs[:limit]).
  38. Find(&posterMaps)
  39. if err != nil {
  40. return err
  41. }
  42. left -= limit
  43. posterIDs = posterIDs[limit:]
  44. }
  45. for _, comment := range comments {
  46. if comment.PosterID <= 0 {
  47. continue
  48. }
  49. var ok bool
  50. if comment.Poster, ok = posterMaps[comment.PosterID]; !ok {
  51. comment.Poster = user_model.NewGhostUser()
  52. }
  53. }
  54. return nil
  55. }
  56. func (comments CommentList) getCommentIDs() []int64 {
  57. ids := make([]int64, 0, len(comments))
  58. for _, comment := range comments {
  59. ids = append(ids, comment.ID)
  60. }
  61. return ids
  62. }
  63. func (comments CommentList) getLabelIDs() []int64 {
  64. ids := make(map[int64]struct{}, len(comments))
  65. for _, comment := range comments {
  66. if _, ok := ids[comment.LabelID]; !ok {
  67. ids[comment.LabelID] = struct{}{}
  68. }
  69. }
  70. return container.KeysInt64(ids)
  71. }
  72. func (comments CommentList) loadLabels(e db.Engine) error {
  73. if len(comments) == 0 {
  74. return nil
  75. }
  76. labelIDs := comments.getLabelIDs()
  77. commentLabels := make(map[int64]*Label, len(labelIDs))
  78. left := len(labelIDs)
  79. for left > 0 {
  80. limit := defaultMaxInSize
  81. if left < limit {
  82. limit = left
  83. }
  84. rows, err := e.
  85. In("id", labelIDs[:limit]).
  86. Rows(new(Label))
  87. if err != nil {
  88. return err
  89. }
  90. for rows.Next() {
  91. var label Label
  92. err = rows.Scan(&label)
  93. if err != nil {
  94. _ = rows.Close()
  95. return err
  96. }
  97. commentLabels[label.ID] = &label
  98. }
  99. _ = rows.Close()
  100. left -= limit
  101. labelIDs = labelIDs[limit:]
  102. }
  103. for _, comment := range comments {
  104. comment.Label = commentLabels[comment.ID]
  105. }
  106. return nil
  107. }
  108. func (comments CommentList) getMilestoneIDs() []int64 {
  109. ids := make(map[int64]struct{}, len(comments))
  110. for _, comment := range comments {
  111. if _, ok := ids[comment.MilestoneID]; !ok {
  112. ids[comment.MilestoneID] = struct{}{}
  113. }
  114. }
  115. return container.KeysInt64(ids)
  116. }
  117. func (comments CommentList) loadMilestones(e db.Engine) error {
  118. if len(comments) == 0 {
  119. return nil
  120. }
  121. milestoneIDs := comments.getMilestoneIDs()
  122. if len(milestoneIDs) == 0 {
  123. return nil
  124. }
  125. milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs))
  126. left := len(milestoneIDs)
  127. for left > 0 {
  128. limit := defaultMaxInSize
  129. if left < limit {
  130. limit = left
  131. }
  132. err := e.
  133. In("id", milestoneIDs[:limit]).
  134. Find(&milestoneMaps)
  135. if err != nil {
  136. return err
  137. }
  138. left -= limit
  139. milestoneIDs = milestoneIDs[limit:]
  140. }
  141. for _, issue := range comments {
  142. issue.Milestone = milestoneMaps[issue.MilestoneID]
  143. }
  144. return nil
  145. }
  146. func (comments CommentList) getOldMilestoneIDs() []int64 {
  147. ids := make(map[int64]struct{}, len(comments))
  148. for _, comment := range comments {
  149. if _, ok := ids[comment.OldMilestoneID]; !ok {
  150. ids[comment.OldMilestoneID] = struct{}{}
  151. }
  152. }
  153. return container.KeysInt64(ids)
  154. }
  155. func (comments CommentList) loadOldMilestones(e db.Engine) error {
  156. if len(comments) == 0 {
  157. return nil
  158. }
  159. milestoneIDs := comments.getOldMilestoneIDs()
  160. if len(milestoneIDs) == 0 {
  161. return nil
  162. }
  163. milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs))
  164. left := len(milestoneIDs)
  165. for left > 0 {
  166. limit := defaultMaxInSize
  167. if left < limit {
  168. limit = left
  169. }
  170. err := e.
  171. In("id", milestoneIDs[:limit]).
  172. Find(&milestoneMaps)
  173. if err != nil {
  174. return err
  175. }
  176. left -= limit
  177. milestoneIDs = milestoneIDs[limit:]
  178. }
  179. for _, issue := range comments {
  180. issue.OldMilestone = milestoneMaps[issue.MilestoneID]
  181. }
  182. return nil
  183. }
  184. func (comments CommentList) getAssigneeIDs() []int64 {
  185. ids := make(map[int64]struct{}, len(comments))
  186. for _, comment := range comments {
  187. if _, ok := ids[comment.AssigneeID]; !ok {
  188. ids[comment.AssigneeID] = struct{}{}
  189. }
  190. }
  191. return container.KeysInt64(ids)
  192. }
  193. func (comments CommentList) loadAssignees(e db.Engine) error {
  194. if len(comments) == 0 {
  195. return nil
  196. }
  197. assigneeIDs := comments.getAssigneeIDs()
  198. assignees := make(map[int64]*user_model.User, len(assigneeIDs))
  199. left := len(assigneeIDs)
  200. for left > 0 {
  201. limit := defaultMaxInSize
  202. if left < limit {
  203. limit = left
  204. }
  205. rows, err := e.
  206. In("id", assigneeIDs[:limit]).
  207. Rows(new(user_model.User))
  208. if err != nil {
  209. return err
  210. }
  211. for rows.Next() {
  212. var user user_model.User
  213. err = rows.Scan(&user)
  214. if err != nil {
  215. rows.Close()
  216. return err
  217. }
  218. assignees[user.ID] = &user
  219. }
  220. _ = rows.Close()
  221. left -= limit
  222. assigneeIDs = assigneeIDs[limit:]
  223. }
  224. for _, comment := range comments {
  225. comment.Assignee = assignees[comment.AssigneeID]
  226. }
  227. return nil
  228. }
  229. // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded
  230. func (comments CommentList) getIssueIDs() []int64 {
  231. ids := make(map[int64]struct{}, len(comments))
  232. for _, comment := range comments {
  233. if comment.Issue != nil {
  234. continue
  235. }
  236. if _, ok := ids[comment.IssueID]; !ok {
  237. ids[comment.IssueID] = struct{}{}
  238. }
  239. }
  240. return container.KeysInt64(ids)
  241. }
  242. // Issues returns all the issues of comments
  243. func (comments CommentList) Issues() IssueList {
  244. issues := make(map[int64]*Issue, len(comments))
  245. for _, comment := range comments {
  246. if comment.Issue != nil {
  247. if _, ok := issues[comment.Issue.ID]; !ok {
  248. issues[comment.Issue.ID] = comment.Issue
  249. }
  250. }
  251. }
  252. issueList := make([]*Issue, 0, len(issues))
  253. for _, issue := range issues {
  254. issueList = append(issueList, issue)
  255. }
  256. return issueList
  257. }
  258. func (comments CommentList) loadIssues(e db.Engine) error {
  259. if len(comments) == 0 {
  260. return nil
  261. }
  262. issueIDs := comments.getIssueIDs()
  263. issues := make(map[int64]*Issue, len(issueIDs))
  264. left := len(issueIDs)
  265. for left > 0 {
  266. limit := defaultMaxInSize
  267. if left < limit {
  268. limit = left
  269. }
  270. rows, err := e.
  271. In("id", issueIDs[:limit]).
  272. Rows(new(Issue))
  273. if err != nil {
  274. return err
  275. }
  276. for rows.Next() {
  277. var issue Issue
  278. err = rows.Scan(&issue)
  279. if err != nil {
  280. rows.Close()
  281. return err
  282. }
  283. issues[issue.ID] = &issue
  284. }
  285. _ = rows.Close()
  286. left -= limit
  287. issueIDs = issueIDs[limit:]
  288. }
  289. for _, comment := range comments {
  290. if comment.Issue == nil {
  291. comment.Issue = issues[comment.IssueID]
  292. }
  293. }
  294. return nil
  295. }
  296. func (comments CommentList) getDependentIssueIDs() []int64 {
  297. ids := make(map[int64]struct{}, len(comments))
  298. for _, comment := range comments {
  299. if comment.DependentIssue != nil {
  300. continue
  301. }
  302. if _, ok := ids[comment.DependentIssueID]; !ok {
  303. ids[comment.DependentIssueID] = struct{}{}
  304. }
  305. }
  306. return container.KeysInt64(ids)
  307. }
  308. func (comments CommentList) loadDependentIssues(ctx context.Context) error {
  309. if len(comments) == 0 {
  310. return nil
  311. }
  312. e := db.GetEngine(ctx)
  313. issueIDs := comments.getDependentIssueIDs()
  314. issues := make(map[int64]*Issue, len(issueIDs))
  315. left := len(issueIDs)
  316. for left > 0 {
  317. limit := defaultMaxInSize
  318. if left < limit {
  319. limit = left
  320. }
  321. rows, err := e.
  322. In("id", issueIDs[:limit]).
  323. Rows(new(Issue))
  324. if err != nil {
  325. return err
  326. }
  327. for rows.Next() {
  328. var issue Issue
  329. err = rows.Scan(&issue)
  330. if err != nil {
  331. _ = rows.Close()
  332. return err
  333. }
  334. issues[issue.ID] = &issue
  335. }
  336. _ = rows.Close()
  337. left -= limit
  338. issueIDs = issueIDs[limit:]
  339. }
  340. for _, comment := range comments {
  341. if comment.DependentIssue == nil {
  342. comment.DependentIssue = issues[comment.DependentIssueID]
  343. if comment.DependentIssue != nil {
  344. if err := comment.DependentIssue.LoadRepo(ctx); err != nil {
  345. return err
  346. }
  347. }
  348. }
  349. }
  350. return nil
  351. }
  352. func (comments CommentList) loadAttachments(e db.Engine) (err error) {
  353. if len(comments) == 0 {
  354. return nil
  355. }
  356. attachments := make(map[int64][]*repo_model.Attachment, len(comments))
  357. commentsIDs := comments.getCommentIDs()
  358. left := len(commentsIDs)
  359. for left > 0 {
  360. limit := defaultMaxInSize
  361. if left < limit {
  362. limit = left
  363. }
  364. rows, err := e.Table("attachment").
  365. Join("INNER", "comment", "comment.id = attachment.comment_id").
  366. In("comment.id", commentsIDs[:limit]).
  367. Rows(new(repo_model.Attachment))
  368. if err != nil {
  369. return err
  370. }
  371. for rows.Next() {
  372. var attachment repo_model.Attachment
  373. err = rows.Scan(&attachment)
  374. if err != nil {
  375. _ = rows.Close()
  376. return err
  377. }
  378. attachments[attachment.CommentID] = append(attachments[attachment.CommentID], &attachment)
  379. }
  380. _ = rows.Close()
  381. left -= limit
  382. commentsIDs = commentsIDs[limit:]
  383. }
  384. for _, comment := range comments {
  385. comment.Attachments = attachments[comment.ID]
  386. }
  387. return nil
  388. }
  389. func (comments CommentList) getReviewIDs() []int64 {
  390. ids := make(map[int64]struct{}, len(comments))
  391. for _, comment := range comments {
  392. if _, ok := ids[comment.ReviewID]; !ok {
  393. ids[comment.ReviewID] = struct{}{}
  394. }
  395. }
  396. return container.KeysInt64(ids)
  397. }
  398. func (comments CommentList) loadReviews(e db.Engine) error {
  399. if len(comments) == 0 {
  400. return nil
  401. }
  402. reviewIDs := comments.getReviewIDs()
  403. reviews := make(map[int64]*Review, len(reviewIDs))
  404. left := len(reviewIDs)
  405. for left > 0 {
  406. limit := defaultMaxInSize
  407. if left < limit {
  408. limit = left
  409. }
  410. rows, err := e.
  411. In("id", reviewIDs[:limit]).
  412. Rows(new(Review))
  413. if err != nil {
  414. return err
  415. }
  416. for rows.Next() {
  417. var review Review
  418. err = rows.Scan(&review)
  419. if err != nil {
  420. _ = rows.Close()
  421. return err
  422. }
  423. reviews[review.ID] = &review
  424. }
  425. _ = rows.Close()
  426. left -= limit
  427. reviewIDs = reviewIDs[limit:]
  428. }
  429. for _, comment := range comments {
  430. comment.Review = reviews[comment.ReviewID]
  431. }
  432. return nil
  433. }
  434. // loadAttributes loads all attributes
  435. func (comments CommentList) loadAttributes(ctx context.Context) (err error) {
  436. e := db.GetEngine(ctx)
  437. if err = comments.loadPosters(e); err != nil {
  438. return
  439. }
  440. if err = comments.loadLabels(e); err != nil {
  441. return
  442. }
  443. if err = comments.loadMilestones(e); err != nil {
  444. return
  445. }
  446. if err = comments.loadOldMilestones(e); err != nil {
  447. return
  448. }
  449. if err = comments.loadAssignees(e); err != nil {
  450. return
  451. }
  452. if err = comments.loadAttachments(e); err != nil {
  453. return
  454. }
  455. if err = comments.loadReviews(e); err != nil {
  456. return
  457. }
  458. if err = comments.loadIssues(e); err != nil {
  459. return
  460. }
  461. if err = comments.loadDependentIssues(ctx); err != nil {
  462. return
  463. }
  464. return nil
  465. }
  466. // LoadAttributes loads attributes of the comments, except for attachments and
  467. // comments
  468. func (comments CommentList) LoadAttributes() error {
  469. return comments.loadAttributes(db.DefaultContext)
  470. }
  471. // LoadAttachments loads attachments
  472. func (comments CommentList) LoadAttachments() error {
  473. return comments.loadAttachments(db.GetEngine(db.DefaultContext))
  474. }
  475. // LoadPosters loads posters
  476. func (comments CommentList) LoadPosters() error {
  477. return comments.loadPosters(db.GetEngine(db.DefaultContext))
  478. }
  479. // LoadIssues loads issues of comments
  480. func (comments CommentList) LoadIssues() error {
  481. return comments.loadIssues(db.GetEngine(db.DefaultContext))
  482. }