如何使用多个子查询编写Rails查找程序

时间:2012-11-21 07:30:46

标签: ruby-on-rails ruby-on-rails-3

这是一个图书馆系统,人们可以在这里借书。每本书都属于一个类别。我们想根据他们最常借的书来给人们一些建议。

以下是四种型号:

class Person < AR
  has_many :borrows
end


class Borrow < AR
  belongs_to :person
  belongs_to :book
end

class Category < AR
  has_many :books
end

class Book < AR
  has_many :borrows
  belongs_to :category
end

我写了SQL来找书


SELECT * FROM books WHERE category_id = 
  (SELECT category_id FROM books WHERE id IN
    (SELECT book_id FROM borrows WHERE person_id =10000)
  GROUP BY category_id ORDER BY count(*) DESC LIMIT 1)
AND id NOT IN
  (SELECT book_id FROM borrows WHERE person_id =10000)

这似乎有效,但我想知道如何用Rails方式编写finder ...

3 个答案:

答案 0 :(得分:2)

你可以做以下事情,亲自写下来.rb

has_many :books, :through => :borrows
has_many :categories_of_books, :through => :books,  :source => :category

&安培;

def suggested_books
 Book.where("category_id IN (?) AND id NOT IN (?)",  self.categories_of_books, self.books)
end

虽然它会导致超过1个查询,但它干净,您只需要这样做:

   @user.suggested_books

答案 1 :(得分:1)

使用活动记录,您可以删除三个子查询中的两个以支持连接:

Book.where(
  category_id: Category.limit(1)
    .joins(:books => :borrows)
    .where("borrows.person_id = ?", 10000)
    .group("categories.id")
    .order("COUNT(*) DESC")
    .pluck("categories.id")
  ).joins(:borrows).where("borrows.person_id != ?", 10000)

仍然不是最佳解决方案,因为它会生成两个单独的查询(一个用于Category上的内部查询)。根据您的需要,这可能不会那么糟糕,例如,如果您决定使用内部查询的结果(相关用户中借用最多的类别)来做其他事情。

答案 2 :(得分:0)

可能是这样的:

@person = Person.find(10000)
@categories = @person.books.map{|b| b.category}.uniq!
@suggestions = @categories.map{|c| c.books} - @person.books

为了让'@ person.books'正常工作,你必须添加你的Person模型:

has_many :books, :through => :borrows