1

Given:

Model: Rating
id | user_id (person being rated) | rated_by | skill_id | rating(int 1..5)

@ratings = Rating.where(:user_id => 1)
1, 1, user1, 1, 5
2, 1, user1, 2, 2
3, 1, user1, 5, 3
4, 1, user2, 1, 5
4, 1, user2, 2, 1
4, 1, user2, 3, 2
...

In ruby (rails), how can I return a JSON object that has the user's average rating per skill_id, something like:

@rating_tally =
 [ skill_id, 
   average rating, 
   number of submission per skill_id, 
   lowest_rating, 
   highest_rating
 ]

Thanks

AnnaSm
  • 2,190
  • 6
  • 22
  • 32

1 Answers1

2

2 Approaches I can think of:

1) logic is processed by Rails server (only one DB call)

# an example implementation
class SomeController < ApplicationController
  def some_action
    json_response = {}

    ratings = Rating.where(user_id: 1)

    ratings.group_by(&:skill_id).each do |skill_id, grouped_ratings|
      lowest_rating = grouped_ratings.max_by(&:rating).rating
      highest_rating = grouped_ratings.min_by(&:rating).rating
      count = grouped_ratings.size
      average_rating = grouped_ratings.sum(&:rating) / count

      json_response[skill_id] = {
        lowest_rating: lowest_rating,
        highest_rating: highest_rating,
        count: count,
        average_rating: average_rating
      }
    end

    render json: json_response
  end
end

the above code should render a JSON response with something like below:

{
  "19": {
    "lowest_rating": 5,
    "highest_rating": 9,
    "count": 4
    "average_rating": 8.5
  },
  "79": {
    "lowest_rating": 4,
    "highest_rating": 10,
    "count": 9
    "average_rating": 9.5
  }
}

where 19 and 79 are skill_ids

2) logic is processed by Database (multiple database calls), but won't take any more processing power usage by the Rails server. I prefer the 1st solution above, so let me know if you prefer this second way, and I'll update my answer. If your memory resource is a constraint / you have thousands of ratings retrieved in this request, probably better to do this second approach for efficiency.

Jay-Ar Polidario
  • 6,463
  • 14
  • 28
  • I appreciate the reply. I like where option 1 is going... The problem is I won't have Rating.where(skill_id: 1) since I won't know how many skills exist per user. When I search for the user's ratings there could be 0,1 or more ratings and I need to loop through all the rating_id's to get a json object per each rating like you show above. Does that make sense? Ideas? – AnnaSm Jun 17 '17 at 22:47
  • Thanks, trying this now... Is `group_by` valid activerecord? I'm seeing group in the docs but not group_by http://guides.rubyonrails.org/active_record_querying.html#group – AnnaSm Jun 18 '17 at 00:17
  • Also are the & characters intentional? example: `&:skill_id` what do they do? I only ask because I'm getting an error `undefined method `[]=' for nil:NilClass` on line `json_response[skill_id] = { ` – AnnaSm Jun 18 '17 at 00:18
  • 1
    `group_by` is part of ruby core. See https://apidock.com/ruby/Enumerable/group_by You use `group` instead of `group_by` if solution 2) because `group` is a database query, while `group_by` is plain Array – Jay-Ar Polidario Jun 18 '17 at 00:19
  • 1
    About &, see this https://stackoverflow.com/questions/1961030/ruby-ampersand-colon-shortcut – Jay-Ar Polidario Jun 18 '17 at 00:24
  • 1
    Basically, it's just a shorthand. i.e. `grouped_ratings.max_by{|r| r.rating}` into `grouped_ratings.max_by(&:rating)` – Jay-Ar Polidario Jun 18 '17 at 00:26
  • Thanks that's very helpful... I'm still trying to figure out why `undefined method `[]=' for nil:NilClass` is happening on this line `json_response[skill_id] = {` I logged above and see that skill_id is being populated – AnnaSm Jun 18 '17 at 00:26
  • 1
    oh you're right I updated my answer and changed into `json_reponse = {}` – Jay-Ar Polidario Jun 18 '17 at 00:27
  • 1
    awesome! no prob :) – Jay-Ar Polidario Jun 18 '17 at 00:28
  • maybe one more question, is there a way to include the skill.title inside the json object? – AnnaSm Jun 18 '17 at 00:30
  • oh never mind, I'll just query for it inside the loop since the Ratings table doesn't have it... Thanks – AnnaSm Jun 18 '17 at 00:32
  • 1
    yes you can, feel free to update the contents of the json_response inside the loop :) – Jay-Ar Polidario Jun 18 '17 at 00:33