3.6 KiB
title, tags, aliases
| title | tags | aliases | ||||
|---|---|---|---|---|---|---|
| Review |
|
|
Review
Polymorphic 1-5 star review. The subjectType discriminator (seller or template) plus subjectId identifies what is being reviewed. sellerId is always present so per-seller aggregations work regardless of subject. A compound unique index on (subjectType, subjectId, reviewerId) prevents a reviewer from posting two reviews for the same subject.
[!note] Source
backend/src/models/Review.ts:19— schema definitionbackend/src/models/Review.ts:38— model export
Schema
| Field | Type | Required | Default | Validation | Index | Description |
|---|---|---|---|---|---|---|
subjectType |
String | yes | — | enum: seller / template |
yes (compound) | Discriminator. |
subjectId |
ObjectId | yes | — | — | yes (compound) | Id of the seller User or RequestTemplate. |
sellerId |
ObjectId → User | yes | — | — | — | Seller associated with the review (always populated). |
reviewerId |
ObjectId → User | yes | — | — | yes (compound + unique) | Author. |
rating |
Number | yes | — | min 1, max 5 | — | Star rating. |
comment |
String | no | "" |
maxlength 1000 | — | Free-form comment. |
isVerifiedBuyer |
Boolean | no | false |
— | — | Whether the reviewer actually bought from this seller. |
purchaseRequestId |
ObjectId → PurchaseRequest | no | null |
— | — | Source request (if any). |
status |
String | no | published |
enum: published / pending / rejected |
— | Moderation status. |
createdAt |
Date | auto | — | — | yes (compound, desc) | Mongoose timestamp. |
updatedAt |
Date | auto | — | — | — | Mongoose timestamp. |
Virtuals
None defined.
Indexes
Defined at backend/src/models/Review.ts:34-36:
{ subjectType: 1, subjectId: 1, createdAt: -1 }— listing for a subject.{ reviewerId: 1, subjectType: 1 }— reviewer history.{ subjectType: 1, subjectId: 1, reviewerId: 1 }— unique, one review per reviewer per subject.
Pre/Post Hooks
None declared.
Instance Methods
None defined.
Static Methods
None defined.
Relationships
- References: User (
sellerId,reviewerId, andsubjectIdwhensubjectType === 'seller'), RequestTemplate (subjectIdwhensubjectType === 'template'), PurchaseRequest (purchaseRequestId). - Referenced by: none.
State Transitions
stateDiagram-v2
[*] --> published : default
[*] --> pending : moderation required
pending --> published : approved
pending --> rejected : rejected
published --> rejected : flagged
rejected --> [*]
Common Queries
// All reviews for a seller
Review.find({ subjectType: 'seller', subjectId: sellerUserId, status: 'published' })
.sort({ createdAt: -1 });
// Average rating per seller
Review.aggregate([
{ $match: { subjectType: 'seller', subjectId: sellerUserId, status: 'published' } },
{ $group: { _id: null, avg: { $avg: '$rating' }, count: { $sum: 1 } } }
]);
// Reviews written by a user
Review.find({ reviewerId: userId }).sort({ createdAt: -1 });
// Reviews for a template
Review.find({ subjectType: 'template', subjectId: templateId, status: 'published' });
[!warning] Duplicate prevention Attempting to insert a second review for the same
(subjectType, subjectId, reviewerId)will fail with aE11000 duplicate keyerror from MongoDB. Application code should treat that as "already reviewed".
Related: User, RequestTemplate, PurchaseRequest.