查询所有后代表的多态关联?

时间:2013-02-26 05:45:35

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

以下是一个人为的例子,但它会得到重点。

假设我有以下型号。

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。

2 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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的工作原理,您就可以调整数据库设计,以便更好地使用您的应用程序。