comment.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. import type { IUser } from '@growi/core/dist/interfaces';
  2. import {
  3. Types, Document, Model, Schema, Query,
  4. } from 'mongoose';
  5. import { IComment } from '~/interfaces/comment';
  6. import { getOrCreateModel } from '~/server/util/mongoose-utils';
  7. import loggerFactory from '~/utils/logger';
  8. const logger = loggerFactory('growi:models:comment');
  9. export interface CommentDocument extends IComment, Document {
  10. removeWithReplies: () => Promise<void>
  11. findCreatorsByPage: (pageId: Types.ObjectId) => Promise<CommentDocument[]>
  12. }
  13. type Add = (
  14. pageId: Types.ObjectId,
  15. creatorId: Types.ObjectId,
  16. revisionId: Types.ObjectId,
  17. comment: string,
  18. commentPosition: number,
  19. replyTo?: Types.ObjectId | null,
  20. ) => Promise<CommentDocument>;
  21. type FindCommentsByPageId = (pageId: Types.ObjectId) => Query<CommentDocument[], CommentDocument>;
  22. type FindCommentsByRevisionId = (revisionId: Types.ObjectId) => Query<CommentDocument[], CommentDocument>;
  23. type FindCreatorsByPage = (pageId: Types.ObjectId) => Promise<IUser[]>
  24. type GetPageIdToCommentMap = (pageIds: Types.ObjectId[]) => Promise<Record<string, CommentDocument[]>>
  25. type CountCommentByPageId = (pageId: Types.ObjectId) => Promise<number>
  26. export interface CommentModel extends Model<CommentDocument> {
  27. add: Add
  28. findCommentsByPageId: FindCommentsByPageId
  29. findCommentsByRevisionId: FindCommentsByRevisionId
  30. findCreatorsByPage: FindCreatorsByPage
  31. getPageIdToCommentMap: GetPageIdToCommentMap
  32. countCommentByPageId: CountCommentByPageId
  33. }
  34. const commentSchema = new Schema<CommentDocument, CommentModel>({
  35. page: { type: Schema.Types.ObjectId, ref: 'Page', index: true },
  36. creator: { type: Schema.Types.ObjectId, ref: 'User', index: true },
  37. revision: { type: Schema.Types.ObjectId, ref: 'Revision', index: true },
  38. comment: { type: String, required: true },
  39. commentPosition: { type: Number, default: -1 },
  40. replyTo: { type: Schema.Types.ObjectId },
  41. }, {
  42. timestamps: true,
  43. });
  44. const add: Add = async function(
  45. this: CommentModel,
  46. pageId,
  47. creatorId,
  48. revisionId,
  49. comment,
  50. commentPosition,
  51. replyTo?,
  52. ): Promise<CommentDocument> {
  53. try {
  54. const data = await this.create({
  55. page: pageId.toString(),
  56. creator: creatorId.toString(),
  57. revision: revisionId.toString(),
  58. comment,
  59. commentPosition,
  60. replyTo,
  61. });
  62. logger.debug('Comment saved.', data);
  63. return data;
  64. }
  65. catch (err) {
  66. logger.debug('Error on saving comment.', err);
  67. throw err;
  68. }
  69. };
  70. commentSchema.statics.add = add;
  71. commentSchema.statics.findCommentsByPageId = function(id) {
  72. return this.find({ page: id }).sort({ createdAt: -1 });
  73. };
  74. commentSchema.statics.findCommentsByRevisionId = function(id) {
  75. return this.find({ revision: id }).sort({ createdAt: -1 });
  76. };
  77. commentSchema.statics.findCreatorsByPage = async function(page) {
  78. return this.distinct('creator', { page }).exec();
  79. };
  80. /**
  81. * @return {object} key: page._id, value: comments
  82. */
  83. commentSchema.statics.getPageIdToCommentMap = async function(pageIds) {
  84. const results = await this.aggregate()
  85. .match({ page: { $in: pageIds } })
  86. .group({ _id: '$page', comments: { $push: '$comment' } });
  87. // convert to map
  88. const idToCommentMap = {};
  89. results.forEach((result, i) => {
  90. idToCommentMap[result._id] = result.comments;
  91. });
  92. return idToCommentMap;
  93. };
  94. commentSchema.statics.countCommentByPageId = async function(page) {
  95. return this.count({ page });
  96. };
  97. commentSchema.methods.removeWithReplies = async function(comment) {
  98. await this.remove({
  99. $or: (
  100. [{ replyTo: this._id }, { _id: this._id }]),
  101. });
  102. };
  103. export const Comment = getOrCreateModel<CommentDocument, CommentModel>('Comment', commentSchema);