简体   繁体   中英

Swift Firebase -Most efficient way to get the total sum of all ratings from observing a ref?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM