1

以下是一个人为的例子,但它会明白这一点。

假设我有以下模型。

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

class Book < ActiveRecord::Base
  has_many :chapters
  has_many :comments, as: :commentable
end

class Chapter < ActiveRecord::Base
  has_many :pages
  has_many :comments, as: :commentable
end

class Page < ActiveRecord::Base
  has_many :paragraphs
  has_many :comments, as: :commentable
end

class Paragraph < ActiveRecord::Base
  has_many :comments, as: :commentable
end

是否可以通过一次查询获得特定书籍及其后代的所有评论?也就是说,我不仅想要书籍模型的所有评论,还想要对章节、页面和段落的评论。顺便说一句,我意识到这本书可以通过每个模型的关联,为每个模型执行内部连接,但这会导致 4 个查询。

我使用 postgres 作为数据库和 Rails 3.2.12。

4

2 回答 2

1

我不知道这是否有帮助,但我正在从数据库方面解决这个问题,并试图找出处理这个问题的最佳方法。我认为您的问题将是您的数据库的外观。如果您可以从 SQL 向后工作,那也可能会有所帮助。

这也是一个可靠的数据库设计将使您的生活更轻松的领域。假设我们有以下表结构:

CREATE TABLE node_class (
     id int not null unique, 
     node_type label primary key, 
     parent_class int references node_class(id)
);

INSERT INTO node_class (id, node_type, parent_class) 
VALUES (1, 'book', null), (2, 'chapter', 1), (3, 'page', 2), (4, 'paragraph', 3);

CREATE TABLE book_node (
    node_id bigserial primary key,
    node_type int not null,
    parent_class int,
    parent_id int,
    .....
    foreign key (parent_class, parent_id) references book_node(id, node_type)
    foreign key (node_type) references node_class (id),
    foreign key (node_type, parent_class) references node_class(id, parent_class)
);
CREATE TABLE book_comments (
    id bigserial primary key,
    comment_text text not null
);

您可能有其他表然后连接到书节点表以提供例如章节、页码等。

然后从那里很容易生成一个构建树然后加入评论的查询:

WITH RECURSIVE bn_tree (node_id, level, path) as
(
   SELECT node_id, 1, node_id::text
     FROM book_node
    where node_id = ?
UNION ALL
   SELECT n.node_id, level + 1, path || ',' || n.node_id::text
     FROM book_node n
     JOIN bn_tree t ON t.node_id = n.parent
)
SELECT c.* 
  FROM book_comments c
  JOIN bn_tree t ON c.node_id = t.node_id;

我认为一旦您了解了 SQL 的工作原理,您就可以调整您的数据库设计以更好地与您的应用程序配合使用。

于 2013-02-26T06:47:55.747 回答
1

如果您正在考虑预先加载,您可以从 Book 端进行,但不能从 Comment 端进行。

>> Book.limit(1).includes(:comments).where('comments.id ...') # you'll probably get a book
>> Comment.limit(1).includes(:commentable).where('books.id ...') # you'll get an error

ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association

您始终可以执行以下操作来获取一本书的评论,

>> book = Book.first
>> comments = book.comments
>> comments = Comment.where(commentable_type: 'Book', commentable_id: book.id)
于 2013-02-26T06:05:40.027 回答