Review List Service
In this lesson, we'll implement the functionality to list restaurant reviews with sorting and pagination capabilities.
Service Implementation
Let's declare the listReviews method in our ReviewService interface:
public interface ReviewService {
// ...
Page<Review> listReviews(String restaurantId, Pageable pageable);
}The method accepts three parameters:
restaurantId- identifies the restaurant whose reviews we want to retrievepageable- contains pagination information like page size, number and sorting preferences
The implementation in ReviewServiceImpl handles sorting and pagination manually because reviews are stored as nested objects within the restaurant document:
@Override
public Page<Review> getRestaurantReviews(String restaurantId, Pageable pageable) {
// Get the restaurant or throw an exception if not found
Restaurant restaurant = getRestaurantOrThrow(restaurantId);
// Create a list of reviews
List<Review> reviews = new ArrayList<>(restaurant.getReviews());
// Apply sorting based on the Pageable's Sort
Sort sort = pageable.getSort();
if (sort.isSorted()) {
Sort.Order order = sort.iterator().next();
String property = order.getProperty();
boolean isAscending = order.getDirection().isAscending();
Comparator<Review> comparator = switch (property) {
case "datePosted" -> Comparator.comparing(Review::getDatePosted);
case "rating" -> Comparator.comparing(Review::getRating);
default -> Comparator.comparing(Review::getDatePosted);
};
reviews.sort(isAscending ? comparator : comparator.reversed());
} else {
// Default sort by date descending
reviews.sort(Comparator.comparing(Review::getDatePosted).reversed());
}
// Calculate pagination boundaries
int start = (int) pageable.getOffset();
// Handle empty pages
if (start >= reviews.size()) {
return new PageImpl<>(Collections.emptyList(), pageable, reviews.size());
}
int end = Math.min((start + pageable.getPageSize()), reviews.size());
// Create the page of reviews
return new PageImpl<>(reviews.subList(start, end), pageable, reviews.size());
}Manual Pagination Considerations
We implement manual pagination because reviews are stored as nested objects within the restaurant document.
Using a separate index for reviews would eliminate the need for manual pagination, but would require:
- Additional data synchronization logic
- More complex data consistency checks
- Increased storage requirements
For our use case, the benefits of keeping reviews nested within restaurants outweigh the cost of manual pagination implementation.
Summary
- Implemented
listReviewsmethod inReviewService - Added sorting options for reviews by date and rating
- Implemented manual pagination for nested review objects
- Reviews are retrieved from the restaurant document