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 方式编写查找器......

4

3 回答 3

2

你可以做以下事情,在 person.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
于 2012-11-21T11:24:14.753 回答
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)。根据您的需要,如果您决定将内部查询的结果(相关用户的借用最多的类别)用于其他事情,这可能不会那么糟糕。

于 2012-11-21T09:24:00.933 回答
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
于 2012-11-21T08:09:52.777 回答