Background
The Community Content team at a large online retailer screens every customer review before it appears on a product page. A rules service decides whether each review is published straight away, held for a human moderator, or rejected. Since the last release, abusive reviews are slipping onto product pages and perfectly good short reviews are being thrown away.
How the system works
Review export
Reviews arrive as blocks of key: value lines. A line that is exactly --- (after trimming) ends a review.
id: R1
product: P100
author: dana
rating: 5
verified: yes
body: Boils a full jug in two minutes.
---
id: R2
...
- Keys are case-insensitive and split from the value at the first
:(so the body may contain colons). Keys and values are trimmed. Unknown keys are ignored. Blank lines and lines starting with#are skipped. Empty blocks (for example two---in a row) are skipped. id,product,author,ratingandbodyare required and must not be empty.ratingis a whole number from 1 to 5.verifiedis optional:yes/trueorno/falsein any case, defaultno.- A non-blank line without
:, an invalid value, a missing field or a duplicate reviewidis aParseError.
Ruleset
banned: scam, counterfeit
watch: refund, broken, cheap
min_words: 4
bannedandwatchtake comma-separated words. Words are trimmed and lower-cased; empty entries are ignored. Repeatedbanned/watchlines add to the list.min_wordsis a whole number from 1 to 100; it defaults to 3.- Keys are case-insensitive. Blank and
#lines are skipped. A line without:or an unknown key is aParseError.
Words
A review's words are the runs of letters, digits and apostrophes in its body, lower-cased; every other character (spaces, punctuation, -, / ...) separates words. "Don't BUY, it's 2x!" has the words don't, buy, it's, 2x. Word lists are compared against these words, so matching is case-insensitive and whole-word only (scampi is not scam).
Moderation rules
moderate(review, rules) checks every rule, in this order, and records the reason of each rule that fires:
| # | Rule | Effect | Reason |
|---|---|---|---|
| 1 | The body contains a banned word | reject | BANNED_WORD |
| 2 | The body has fewer than min_words words (exactly min_words is fine) | reject | TOO_SHORT |
| 3 | The body contains http://, https:// or www. (any case) | hold | LINK |
| 4 | The author is not verified and the rating is 1 or 5 | hold | UNVERIFIED_EXTREME |
| 5 | The body contains two or more watch words (each occurrence counts, so refund refund is two) | hold | WATCH_WORDS |
The status is REJECT if any reject rule fired, else HOLD if any hold rule fired, else PUBLISH. reasons lists every rule that fired, in the order above (empty when published).
Moderation queue
runQueue(reviews, rules) moderates reviews in input order:
- A review whose product and author (author compared case-insensitively) match an earlier review in the same queue is rejected with the single reason
DUPLICATE; no other rules are checked for it. The earlier review counts whatever its own status was. - Counts of published, held and rejected reviews;
heldIdsandrejectedIdsin input order; every verdict by review id. products: for each product with at least one published review, the number published andaverageTenths, the mean star rating × 10 rounded half up (4.25 stars → 43, 3.666… → 37).
Project structure
include/
util.h ParseError, trim / toLower / split, words, number and yes/no parsing
review.h Review, review export parser
rules.h Ruleset, ruleset parser
moderator.h Status, Verdict, containsLink, moderate
queue.h ProductSummary, QueueReport, runQueue
src/ implementations of the above
tests/ unit tests (testlib.h is the test framework, runner.cpp the entry point, fixtures.h shared data)
Your task
Moderators have reported problems since the last release:
- The project no longer builds.
- Reviews that shout a banned word in capitals ("SCAM") are published instead of rejected.
- Short but perfectly fine reviews such as "Works exactly as described." are rejected even though they meet the minimum length.
Find and fix the defects so that all tests pass.
- Do not modify files in
tests/. - Keep your changes minimal. Fix the defects; don't rewrite modules.
- Additional hidden tests check the behaviour described in this README.