My database structure is like this:
Restaurants
@-restaurantId_0
@-reviewId_1
-rating: 1
-user: uid_A
@-reviewId_2
-rating: 0.5
-user: uid_B
@-reviewId_3
-rating: 3
-user: uid_C
@-restaurantId_1
@-reviewId_4
-rating: 4
-user: uid_A
@-reviewId_5
-rating: 2.5
-user: uid_S
@-restaurantId_2
// ...
@-restaurantId_3
// ...
I have a star rating system and each user can leave a rating for a restaurant. I record the rating they left and their uid under that review.
When displaying a specific restaurant and it's stars I need to grab all the ratings from each review (for that restaurant) and then run some calculations to determine the restaurants total rating:
allTheRatingsAddedUp / totatNumOfReviews
For eg. in my database structure above the restaurant with restaurantId_0 has 3 reviews and all the ratings add up to 4.5 so it would have a 1.5 star rating in total ( 4.5 / 3 ).
I can get this information in 2 different ways using the below methods. In both ways I have to loop through all the reviews to get the sum of all the ratings. As you can see below there is a lot of code involved to get the sum.
Is there a more efficient way to achieve the sum of all the ratings without using so much code?
Would a transaction block work better in this situation? From my understanding a transaction block would keep track of the ratings so even if a user was looking at restaurantId_0 with a 1.5 rating
if 3 other users left a high rating a transaction block would reflect that immediately and the star rating would change (after the new calculations are ran)
1st way
Database.database().reference()
.child("Restaurants")
.child("restaurantId_0")
.observeSingleEvent(of: .value, with: { (snapshot) in
if !snapshot.exists() {
return
}
let totatNumOfReviews = snapshot.childrenCount
var arrOfReviews = [ReviewModel]()
var allTheRatingsAddedUp = 0.0
guard let dictionaries = snapshot.value as? [String: Any] else { return }
var count = 0
dictionaries.forEach({ (key, value) in
guard let dict = value as? [String: Any] else { return }
var review = ReviewModel(dict: dict)
review.reviewId = key
let isContained = arrOfReviews.contains(where: { (containedReview) -> Bool in
return review.reviewId == containedReview.reviewId
})
if !isContained {
arrOfReviews.append(review)
allTheRatingsAddedUp += review.rating ?? 0
count += 1
if count == totatNumOfReviews {
// run calculations on: allTheRatingsAddedUp / totatNumOfReviews
}
}
})
})
2nd way
Database.database().reference()
.child("Restaurants")
.child("restaurantId_0")
.observeSingleEvent(of: .value) { (snapshot) in
if !snapshot.exists() {
return
}
let totatNumOfReviews = snapshot.childrenCount
var arrOfReviews = [ReviewModel]()
var allTheRatingsAddedUp = 0.0
Database.database().reference()
.child("Restaurants")
.child("restaurantId_0")
.observe( .childAdded, with: { (snapshot) in
if let dict = snapshot.value as? [String: Any] {
let review = ReviewModel(dict: dict)
arrOfReviews.append(review)
allTheRatingsAddedUp += review.rating ?? 0
if arrOfReviews.count == totatNumOfReviews {
// run calculations on: allTheRatingsAddedUp / totatNumOfReviews
}
}
})
The basic approach of your first looks fine, so I doubt there's a more efficient way, although your code looks a bit convoluted.
Database.database().reference()
.child("Restaurants")
.child("restaurantId_0")
.observeSingleEvent(of: .value, with: { (snapshot) in
let reviewCount = snapshot.childrenCount
var ratingSum = 0.0
for review in snapshot.children.allObjects as? [DataSnapshot] {
guard let dict = review.value as? [String: Any]
ratingSum = ratingSum + dict.rating ?? 0
}
print(ratingSum / reviewCount)
})
Your second approach may work but is non-idiomatic, since you're nesting a second listener on the same data that you already loaded.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.