【问题标题】:Active Record: find records by ceratin conditionActive Record:按特定条件查找记录
【发布时间】:2018-04-17 08:31:13
【问题描述】:

我的目标是找到三个有超过 1 条评论且平均评分 >= 4 的医生

目前我正在使用这项服务

class RatingCounterService
  def get_three_best_doctors
    doctors = find_doctors_with_reviews
    sorted_doctors = sort_doctors(doctors)
    reversed_hash = reverse_hash_with_sorted_doctors(sorted_doctors)
    three_doctors = get_first_three_doctors(reversed_hash)
  end

  private

  def find_doctors_with_reviews
    doctors_with_reviews = {}

    Doctor.all.each do |doctor|
      if doctor.reviews.count > 0 && doctor.average_rating >= 4
        doctors_with_reviews[doctor] = doctor.average_rating
      end
    end

    doctors_with_reviews
  end

  def sort_doctors(doctors)
    doctors.sort_by { |doctor, rating| rating }
  end

  def reverse_hash_with_sorted_doctors(sorted_doctors)
    reversed = sorted_doctors.reverse_each.to_h
  end

  def get_first_three_doctors(reversed_hash)
    reversed_hash.first(3).to_h.keys
  end
end

这很慢。

我的医生模型:

class Doctor < ApplicationRecord
  has_many :reviews, dependent: :destroy

  def average_rating
    reviews.count == 0 ? 0 : reviews.average(:rating).round(2)
  end
end

审查模型:

class Review < ApplicationRecord
  belongs_to :doctor

  validates :rating, presence: true
end

我可以通过此请求找到所有有超过 1 条评论的医生

doctors_with_reviews = Doctor.joins(:reviews).group('doctors.id').having('count(doctors.id) > 0')

但是,如果“平均评分”是实例方法,我如何找到平均评分 >= 4 的医生并按最高评分排序?

【问题讨论】:

  • 为了找到"with 1 or more"评论,所有你需要做的是Doctor.joins(:reviews)。为了找到 "with more than 1"(这是你写的),你实际上需要做 Doctor.joins(:reviews).group('doctors.id').having('count(doctors.id) &gt; 1').
  • But how can I find doctors with an average rating &gt;= 4 and order them by the highest rating if the "average rating" is an instance method? 你可以简单地尝试Doctor.includes(:reviews).where("reviews.avarage(:rating).round(2) &gt;= ?", 4).group('doctors.id').having('count(doctors.id) &gt; 0') 或在上面创建一个范围并使用范围

标签: ruby-on-rails ruby activerecord


【解决方案1】:

感谢这个答案:highest_rated scope to order by average rating

我的最终解决方案是

Doctor.joins(:reviews).group('doctors.id').order('AVG(reviews.rating) DESC').limit(3)

【讨论】: