Start a new topic
Answered

How to solve this?

Even tough I'm working with Kinveys HTML/Javascript API my question is of general purpose:


Let's say there is a collection called PRODUCTS holding a list of simple products with name, description, type, price, etc.


Users now can rate these products within a range from 1 (worst) to 5 (best). All the user ratings are stored in collection RATINGS. 


As you can think there is a 1:many relation from a PRODUCTS to RATINGS (a product can be have one or many ratings). 


Requirement:Whenever a user requests a product list from PRODUCTS collection he also should see the average rating of each product in that list.


In a relational database this an easy task but I don't know what's the best way to do that in Kinvey? I'm aware there are grouping and average functions in Kinvey API. So the math part is not the point. I wonder how I should "join" the data to get the average rating of each request product. Consider there could be like 1'000 ratings for a single product and a user may request 100 distinct products. 


I'm open for any idea to solve this in a proper and effective/fast way.


Regards


Best Answer

Hi Tayger,

It mostly depends what your expected usage pattern is - whether GET request are more, whether you expect to have more request to the PRODUCTS collection or to the USER collection (retrieving users that made the rating).


If you mainly expect to retrieve the average rating each product has, and rarely to retrieve all the ratings a user has made, then storing the ratings as an array in the PRODUCTS would be a good solution as you would retrieve both the Products and the Ratings in a single request.


Another solution would be to use a separate Ratings collection and to store just the the calculated average for each product in the Products collections. This requires that when a new rating is added by a user, you would:

1) add the rating to the RATINGS collection

2) calculate the new average and update that number in the Products collection for the given item.

The benefit of this approach is that you would not have to calculate the average each time someone makes a GET request but you would calculate it just once - when new entry is added.


Regards

Martin Apostolov


A word after in case someone else needs something similar. I was able to create the needed Business Logic that now does the job:

1. POSTSAVE (on collection RATINGS): Get the product ID from the inserted/updated user rating.

2. Get all ratings of this inserted/updated product ID rating from RATINGS

3. Iterate through them to sum them up and divide the result by all found ratings of this product.

4. Replace the old rating of the related product in collection PRODUCT by the new calculated product. This can be done in two ways:

a) Read the product record from PRODUCT, replace the rating, store the product record back to PRODUCT

b) Pranav gave once a good hint in another post: Use $SET to modify single attributes inside a collection (works only in backend logic):

modules.collectionAccess.collection("products").update({"_id": <product_id>}, {"$set": { "product.rating": <average>, "product.amount": <amount>} }, function(err, result) {} );

The $set command will only modify the product.rating (column product > key value of rating in stored object and product.amount (column product > key value of amount (amount of ratings) in stored object).

I would have wanted the Business Logic would/could do the average calculation AND the count (amount of ratings) but that seems not be possible in one step. Therefore I let Javascript do the job, no big thing.


I'm aware Business Logic shouldn't be used anymore but it's very powerful/easy to use while FlexService needs some local installation (as far as I've understood). So, Kinvey, please keep Business Logic. ;)


Regards

Tayger

Hi Martin


Wow, thanks for the answer! The last view days I was thinking around the solution you provided at last. That's now how exactly I will do it! There will be way more GETs than new ratings. So therefore this solution makes sense. I'll do the math with Kinvey's backend logic. Excellent, thank you!


Tayger

Answer

Hi Tayger,

It mostly depends what your expected usage pattern is - whether GET request are more, whether you expect to have more request to the PRODUCTS collection or to the USER collection (retrieving users that made the rating).


If you mainly expect to retrieve the average rating each product has, and rarely to retrieve all the ratings a user has made, then storing the ratings as an array in the PRODUCTS would be a good solution as you would retrieve both the Products and the Ratings in a single request.


Another solution would be to use a separate Ratings collection and to store just the the calculated average for each product in the Products collections. This requires that when a new rating is added by a user, you would:

1) add the rating to the RATINGS collection

2) calculate the new average and update that number in the Products collection for the given item.

The benefit of this approach is that you would not have to calculate the average each time someone makes a GET request but you would calculate it just once - when new entry is added.


Regards

Martin Apostolov


1 person likes this
Login or Signup to post a comment