Machine Coding Problem

Review & Rating System

macoAllcommerceweighted-averagespam-filter
Commonly Asked By:YelpAmazonGoogleTripAdvisor

Functional Specifications (In-Scope)

  • Rating Submissions: Processes 1-5 star ratings paired with descriptive comments.
  • Verified Purchase Badges: Interfaces with delivery registries to lock a green verified stamp on valid reviews.
  • Abusive Spam Blockades: Evaluates velocity checks and text similarities to suppress duplicate reviewer patterns.
  • Calculated Weighted Star Ratings: Weights verified customer ratings at 1.5x relative to unverified users.

Out-of-Scope Boundaries

  • Full-Text Semantic Translation: Multi-lingual translator pipelines are decoupled.
  • Direct User Social Follows: Does not record social graphs between different reviewers.

Production reference implementations demonstrating duplicate checkers, verified tags, and aggregate calculations:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

enum ReviewStatus {
    ACTIVE,
    FLAGGED_SPAM,
    UNDER_REVIEW
}

class Review {
    private final String reviewId;
    private final String userId;
    private final String productId;
    private final int rating;
    private final String reviewText;
    private final boolean isVerified;
    private ReviewStatus status;
    private final long createdAt;
    private final AtomicInteger upvotes = new AtomicInteger(0);
    private final AtomicInteger downvotes = new AtomicInteger(0);

    public Review(String userId, String productId, int rating, String reviewText, boolean isVerified) {
        this.reviewId = UUID.randomUUID().toString();
        this.userId = userId;
        this.productId = productId;
        this.rating = rating;
        this.reviewText = reviewText;
        this.isVerified = isVerified;
        this.status = ReviewStatus.ACTIVE;
        this.createdAt = System.currentTimeMillis();
    }

    public String getReviewId() { return reviewId; }
    public String getUserId() { return userId; }
    public String getProductId() { return productId; }
    public int getRating() { return rating; }
    public String getReviewText() { return reviewText; }
    public boolean isVerified() { return isVerified; }
    public ReviewStatus getStatus() { return status; }
    public void setStatus(ReviewStatus status) { this.status = status; }
    public long getCreatedAt() { return createdAt; }

    public int getUpvotes() { return upvotes.get(); }
    public int getDownvotes() { return downvotes.get(); }

    public void upvote() { upvotes.incrementAndGet(); }
    public void downvote() { downvotes.incrementAndGet(); }

    public double getHelpfulScore() {
        int up = upvotes.get();
        int down = downvotes.get();
        if (up + down == 0) return 0.0;
        return (double) up / (up + down);
    }
}

interface SpamFilter {
    boolean isSpam(Review review);
}

class ContentDuplicationFilter implements SpamFilter {
    private final Map<String, List<String>> userReviewTexts = new ConcurrentHashMap<>();

    @Override
    public boolean isSpam(Review review) {
        List<String> existing = userReviewTexts.computeIfAbsent(review.getUserId(), u -> new CopyOnWriteArrayList<>());
        for (String text : existing) {
            if (calculateSimilarity(text, review.getReviewText()) > 0.8) {
                return true;
            }
        }
        existing.add(review.getReviewText());
        return false;
    }

    private double calculateSimilarity(String s1, String s2) {
        if (s1 == null || s2 == null) return 0.0;
        Set<String> words1 = new HashSet<>(Arrays.asList(s1.toLowerCase().split("\\s+")));
        Set<String> words2 = new HashSet<>(Arrays.asList(s2.toLowerCase().split("\\s+")));
        
        Set<String> intersection = new HashSet<>(words1);
        intersection.retainAll(words2);
        
        Set<String> union = new HashSet<>(words1);
        union.addAll(words2);
        
        if (union.isEmpty()) return 0.0;
        return (double) intersection.size() / union.size();
    }
}

class VelocityAbuseFilter implements SpamFilter {
    private final Map<String, Long> userLastSubmitTime = new ConcurrentHashMap<>();
    private static final long COOLDOWN_MS = 5000;

    @Override
    public boolean isSpam(Review review) {
        long now = System.currentTimeMillis();
        Long lastSubmit = userLastSubmitTime.get(review.getUserId());
        if (lastSubmit != null && (now - lastSubmit) < COOLDOWN_MS) {
            return true;
        }
        userLastSubmitTime.put(review.getUserId(), now);
        return false;
    }
}

class RatingAggregation {
    private final double weightedAverage;
    private final double simpleAverage;
    private final int totalCount;

    public RatingAggregation(double weightedAverage, double simpleAverage, int totalCount) {
        this.weightedAverage = weightedAverage;
        this.simpleAverage = simpleAverage;
        this.totalCount = totalCount;
    }

    public double getWeightedAverage() { return weightedAverage; }
    public double getSimpleAverage() { return simpleAverage; }
    public int getTotalCount() { return totalCount; }
}

class RatingAggregator {
    public RatingAggregation aggregate(Collection<Review> reviews) {
        double sumVerified = 0;
        int countVerified = 0;
        double sumUnverified = 0;
        int countUnverified = 0;

        for (Review r : reviews) {
            if (r.getStatus() != ReviewStatus.ACTIVE) {
                continue;
            }
            if (r.isVerified()) {
                sumVerified += r.getRating();
                countVerified++;
            } else {
                sumUnverified += r.getRating();
                countUnverified++;
            }
        }

        int totalCount = countVerified + countUnverified;
        if (totalCount == 0) {
            return new RatingAggregation(0.0, 0.0, 0);
        }

        double weightedNum = (1.5 * sumVerified) + sumUnverified;
        double weightedDen = (1.5 * countVerified) + countUnverified;
        double weightedAverage = weightedNum / weightedDen;

        double simpleAverage = (sumVerified + sumUnverified) / totalCount;

        return new RatingAggregation(weightedAverage, simpleAverage, totalCount);
    }
}

class ReviewRanker {
    public List<Review> sort(List<Review> reviews) {
        List<Review> sorted = new ArrayList<>(reviews);
        sorted.sort((r1, r2) -> {
            if (r1.getStatus() != r2.getStatus()) {
                return r1.getStatus() == ReviewStatus.ACTIVE ? -1 : 1;
            }
            int compareVotes = Double.compare(r2.getHelpfulScore(), r1.getHelpfulScore());
            if (compareVotes != 0) {
                return compareVotes;
            }
            return Long.compare(r2.getCreatedAt(), r1.getCreatedAt());
        });
        return sorted;
    }
}

class ReviewManager {
    private final Map<String, List<Review>> productReviews = new ConcurrentHashMap<>();
    private final List<SpamFilter> spamFilters = new CopyOnWriteArrayList<>();
    private final RatingAggregator aggregator = new RatingAggregator();
    private final ReviewRanker ranker = new ReviewRanker();

    public void addSpamFilter(SpamFilter filter) {
        spamFilters.add(filter);
    }

    public boolean submitReview(Review review) {
        for (SpamFilter filter : spamFilters) {
            if (filter.isSpam(review)) {
                review.setStatus(ReviewStatus.FLAGGED_SPAM);
                System.out.println("🚨 SPAM ALERT: Review by " + review.getUserId() + " flagged as spam.");
                productReviews.computeIfAbsent(review.getProductId(), p -> new CopyOnWriteArrayList<>()).add(review);
                return false;
            }
        }
        productReviews.computeIfAbsent(review.getProductId(), p -> new CopyOnWriteArrayList<>()).add(review);
        System.out.println("✅ REVIEW ACCEPTED: Review by " + review.getUserId() + " successfully submitted.");
        return true;
    }

    public RatingAggregation getProductRating(String productId) {
        List<Review> reviews = productReviews.getOrDefault(productId, Collections.emptyList());
        return aggregator.aggregate(reviews);
    }

    public List<Review> getSortedReviews(String productId) {
        List<Review> reviews = productReviews.getOrDefault(productId, Collections.emptyList());
        return ranker.sort(reviews);
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== JAVA REVIEW & RATING SYSTEM SIMULATION ===");
        ReviewManager manager = new ReviewManager();
        manager.addSpamFilter(new ContentDuplicationFilter());
        manager.addSpamFilter(new VelocityAbuseFilter());

        String productId = "PROD-100";

        Review r1 = new Review("alice_dev", productId, 5, "Absolutely outstanding quality. Easily worth the price.", true);
        Review r2 = new Review("dan_commerce", productId, 4, "Very solid construction. A bit tight on the ears but overall great aviators.", true);
        Review r3 = new Review("unverified_guy", productId, 2, "Decent glasses but delivery took too long.", false);

        manager.submitReview(r1);
        manager.submitReview(r2);
        manager.submitReview(r3);

        r1.upvote(); r1.upvote(); r1.upvote(); r1.downvote();
        r2.upvote(); r2.upvote();

        RatingAggregation aggBefore = manager.getProductRating(productId);
        System.out.println("\nInitial Aggregated Rating:");
        System.out.println(" - Total Active Reviews: " + aggBefore.getTotalCount());
        System.out.printf(" - Simple Average Rating: %.2f\n", aggBefore.getSimpleAverage());
        System.out.printf(" - Weighted Average Rating: %.2f (Verified purchases given 1.5x weight)\n", aggBefore.getWeightedAverage());

        System.out.println("\nSubmitting spam review with same content from same user...");
        Review rSpamDupe = new Review("alice_dev", productId, 5, "Absolutely outstanding quality. Easily worth the price.", true);
        manager.submitReview(rSpamDupe);

        System.out.println("\nSubmitting rapid consecutive reviews (velocity check)...");
        Review rVelocity1 = new Review("fast_typer", productId, 5, "Great product!", false);
        Review rVelocity2 = new Review("fast_typer", productId, 5, "Loved the packaging too!", false);
        manager.submitReview(rVelocity1);
        manager.submitReview(rVelocity2);

        System.out.println("\nSorted Active Reviews by Helpfulness & Date:");
        List<Review> sorted = manager.getSortedReviews(productId);
        for (Review r : sorted) {
            if (r.getStatus() == ReviewStatus.ACTIVE) {
                System.out.printf(" - User: %s | Rating: %d | Helpful Ratio: %.2f | Verified: %s | Text: \"%s\"\n",
                    r.getUserId(), r.getRating(), r.getHelpfulScore(), r.isVerified(), r.getReviewText());
            }
        }

        System.out.println("=== END OF JAVA SIMULATION ===");
    }
}