Using Reviews to Rate Products and Channels
Rating value
Let's assume the rating interface is using a system with 5 stars. The rating field of a review is a number between -100 and 100.
We first have to know how to encode the number of stars in the rating field.
| Number of stars | Rating value |
|---|---|
| ★★★★★ | 5 |
| ★★★★☆ | 4 |
| ★★★☆☆ | 3 |
| ★★☆☆☆ | 2 |
| ★☆☆☆☆ | 1 |
| ☆☆☆☆☆ | 0 |
Here, we use the rating field to represent the number of stars.
We could also use this field to store a percentage (ex: 95 would mean 95 %), or to store a like/dislike information (1 would mean like, -1 would mean dislike).
Review approval process
If you do not need any approval process, skip this part
If we have an approval process for a review to be used for a product or a channel, we model the approval process with a state machine.
First of all, we create the approved state:
POST /<project-key>/states with:
final StateDraftDsl approvedStateDraft = StateDraftBuilder.of("approved", StateType.REVIEW_STATE).roles(asSet(StateRole.REVIEW_INCLUDED_IN_STATISTICS)).build();return client.execute(StateCreateCommand.of(approvedStateDraft));
Then we create the initial to-approve state, which has a possible transition to the approved state:
POST /<project-key>/states with:
final StateDraftDsl stateDraft = StateDraftBuilder.of("to-approve", StateType.REVIEW_STATE).initial(true).transitions(asSet(approvedState.toReference())).build();return client.execute(StateCreateCommand.of(stateDraft));
Creating Reviews
Now we can create a review in the initial state to-approve:
POST /<project-key>/reviews with:
final ResourceIdentifier<State> stateResourceIdentifier = ResourceIdentifier.ofKey(state.getKey());final ResourceIdentifier<Product> target = ResourceIdentifier.ofId(product.getId(), "product");final ReviewDraftDsl reviewDraft = ReviewDraftBuilder.ofRating(4).key("review-1").state(stateResourceIdentifier).target(target).build();return client.execute(ReviewCreateCommand.of(reviewDraft));
remove the field state if you have not created any state machine first
Query which Reviews should be approved
skip this part if you do not have any approval process
We can query which reviews should be approved with a where predicate
GET /<project-key>/reviews with query parameters:
where=state(id in ("<id-of-to-approve-state>"))
final ReviewQuery reviewQuery = ReviewQuery.of().withPredicates(m -> m.state().id().is(state.getId()));return client.execute(reviewQuery);
Approving a Review
skip this part if you do not have any approval process
We can now approve the review review-1:
POST /<project-key>/reviews/key=review-1 with:
final ReviewUpdateCommand reviewUpdateCommand = ReviewUpdateCommand.of(review, TransitionState.of(approvedState));return client.execute(reviewUpdateCommand);
Displaying Products
We can display all products:
- that have at least 3 stars (average rating superior to 3)
- with facets about the number of products rated with an average in the different ranges 0 to 1 star, 1 to 2 stars, 2 to 3 stars, 3 to 4 stars and 4 to 5 stars.
- sorted by average ratings
For that, we use the search endpoint:
GET /<project-key>/product-projections/search with query parameters:
filter = reviewRatingStatistics.averageRating:range (3 to *)facet = reviewRatingStatistics.averageRating:range (0 to 1), (1 to 2), (2 to 3), (3 to 4), (4 to 5)sort = reviewRatingStatistics.averageRating desc
final ProductProjectionSearch productSearchWithFilter = ProductProjectionSearch.ofCurrent().plusQueryFilters(m -> m.reviewRatingStatistics().averageRating().isGreaterThanOrEqualTo(BigDecimal.valueOf(3))).plusFacets(m -> m.reviewRatingStatistics().averageRating().allRanges()).plusSort(m -> m.reviewRatingStatistics().averageRating().desc());return client.execute(productSearchWithFilter);
The response gives us the following information:
{"offset": 0,"count": <number of products to display>,"total": <total number of products with an average rating superior to 3>,"results": [{"id": "<product-1-id>",[...]"reviewRatingStatistics": {"averageRating": 4.07037,"highestRating": 5,"lowestRating": 3,"count": 1009,"ratingsDistribution": {"5": 254,"4": 572,"3": 183}}},{"id": "<product-2-id>",[...]"reviewRatingStatistics": {"averageRating": 2.97677,"highestRating": 1,"lowestRating": 0.2,"count": 3875,"ratingsDistribution": {"4": 145,"3": 3495,"2": 235}}},[...]],"facets": {"reviewRatingStatistics.averageRating": {"type": "range","dataType": "number","ranges": [{"from": 0.0,"to": 1.0,"count": 0},{"from": 1.0,"to": 2.0,"count": 15},{"from": 2.0,"to": 3.0,"count": 78},{"from": 3.0,"to": 4.0,"count": 242},{"from": 4.0,"to": 5.0,"count": 145}]}}}
Displaying one Product
The following information can be found in the JSON data of one product:
- average rating:
reviewRatingStatistics.averageRating.
The value is already rounded to 5 decimals. Depending on your need, you may have to round it more. - number of reviews:
reviewRatingStatistics.count. - distribution of ratings:
reviewRatingStatistics.ratingsDistribution:
★★★★★:reviewRatingStatistics.ratingsDistribution.5
★★★★☆:reviewRatingStatistics.ratingsDistribution.4
★★★☆☆:reviewRatingStatistics.ratingsDistribution.3
★★☆☆☆:reviewRatingStatistics.ratingsDistribution.2
★☆☆☆☆:reviewRatingStatistics.ratingsDistribution.1
☆☆☆☆☆:reviewRatingStatistics.ratingsDistribution.0
If one field for one rating does not exist, it means that there are no reviews with that rating value.
Displaying all Reviews of one Product
To retrieve all reviews for one product, sorted by rating:
GET /<project-key>/reviews with query parameter:
where = target(typeId = "product" and id = "<product-id>")sort = rating desc
final ReviewQuery reviewQuery = ReviewQuery.of().withPredicates(m -> m.target().typeId().is("product").and(m.target().id().is(product.getId()))).plusSort(m -> m.rating().sort().desc());return client.execute(reviewQuery);
To query only the reviews used for the rating statistics of the product:
GET <project-key>/reviews with query parameters:
where = target(typeId = "product" and id = "<product-id>") and includedInStatistics = truesort = rating desc
final ReviewQuery reviewQuery = ReviewQuery.of().withPredicates(m -> m.target().typeId().is("product").and(m.target().id().is(product.getId())).and(m.includedInStatistics().is(true))).plusSort(m -> m.rating().sort().desc());return client.execute(reviewQuery);