rails 祖先分页

问题描述 投票:0回答:2

我刚刚学习了 Railscast 教程:

http://railscasts.com/episodes/262-trees-with-ancestry

是否可以对已安排的 Ancestry 结果进行分页? 例如:鉴于我的消息控制器中有以下内容:

def index
  @messages = Message.arrange(:order => :name)
end

那么我将如何对它进行分页,因为它会导致散列?

更新 我发现如果我使用 .keys 那么它会分页,但只有顶层而不是孩子。

Message.scoped.arrange(:order => :name).keys

更新 每条消息都有一个代码和一些内容。我可以嵌套消息

假设我有

代码-姓名

1 - Test1
  1 - test1 sub1
  2 - test1 sub2
2 - Test2
  1 - test2 sub1
  2 - test2 sub2
  3 - test2 sub3

这就是我想要显示列表的方式,但我也想对这个排序树进行分页。

ruby-on-rails-3 tree will-paginate pagination
2个回答
4
投票

这是可能的,但我只设法使用两次数据库访问来做到这一点。

主要问题源于无法对节点的子节点设置限制,这导致节点的子节点被截断或后续页面上的子节点被孤立。

例子:

id: 105, Ancestry: Null
id: 117, Ancestry: 105
id: 118, Ancestry: 105/117
id: 119, Ancestry: 105/117/118

LIMIT 0,3(为了上面的示例)将返回前三个记录,这将呈现除 id:119 之外的所有记录。随后的 LIMIT 3,3 将返回 id: 119,由于其父项不存在,因此无法正确呈现。

我采用的一个解决方案是使用两个查询:

  1. 第一个只返回根节点。这些可以排序,并且是分页的查询。
  2. 基于第一个查询发出第二个查询,它返回分页父母的所有孩子。您应该能够按级别对孩子进行排序。

就我而言,我有一个 Post 模型(具有_ancestry)。每个帖子都可以有任何级别的回复。 post 对象也有一个回复计数,它是它的直接子对象的缓存计数器。

在控制器中:

roots  = @topic.posts.roots_only.paginate :page => params[:page]
@posts = Post.fetch_children_for_roots(@topic, roots)

在Post模型中:

named_scope :roots_only, :conditions => 'posts.ancestry is null'

def self.fetch_children_for_roots(postable, roots)
  unless roots.blank?
    condition = roots.select{|r|r.replies_count > 0}.collect{|r| "(ancestry like '#{r.id}%')"}.join(' or ')
    unless condition.blank?
      children = postable.posts.scoped(:from => 'posts FORCE INDEX (index_posts_on_ancestry)', :conditions => condition).all
      roots.concat children
    end
  end
  roots
end

一些注意事项:

  • 如果使用多个 LIKE 语句,MySQL 将停止使用祖先列索引。 FORCE INDEX 强制 mySQL 使用索引并防止全表扫描
  • LIKE 语句仅适用于有直接子节点的节点,因此 replies_count 列派上用场了
  • 类方法所做的是将子项附加到根,这是一个 WillPaginate::Collection

最后,这些都可以在你的视图中进行管理:

  =will_paginate @posts  
  -Post.arrange_nodes(@posts).each do |post, replies|
    =do stuff here

这里的关键方法是 arrange_nodes,它从祖先插件中混合到您的模型中。这基本上需要一个排序的节点数组并返回一个排序的和分层的哈希。

我明白这种方法不能直接解决你的问题,但我希望同样的方法,经过调整,可以应用于你的案例。

可能有一种更优雅的方法来做到这一点,但总的来说我对这个解决方案很满意(直到出现更好的解决方案)。


0
投票

首先:

假设您按创建日期排序,您可以按

id
排序。它适用于绝大多数情况。

排序:

如果您按

ancestry
(物化路径列)排序,那么您将所有根节点放在前面,然后是子节点,之后可能有数百条记录。

所以你想按祖先和身份一起排序:

coalesce(ancestry, '0') || '/' || id     -- postgres, sqlite 
concat(coalesce(ancestry, '0'), '/', id) -- postgres, mysql

记录将按正确顺序排列,您可以从那里以常规方式分页。

红宝石:

class TopicController
  def show
    @posts  = @topic.posts.chronological.paginate :page => params[:page]
  end
end

class Post
  scope :chronological, -> { order("coalesce(ancestry,'0')||'/'|| id") }
end

索引:

在postgres中,您可以在

post_id, id, ancestry
post_id, order("coalesce(ancestry,'0')||'/'|| id")
上创建索引。

整理:

还要记住,对于较新的数据库(2018 年之后),它们会使用较新的规则进行排序/整理。这些往往会忽略标点符号。

您想使用 ascii 规则进行排序。无论是在列上使用二进制列、ascii 字符集(仅限 mysql)还是 POSIX / C 排序规则。

我试图让祖先宝石中的整理信息更加清晰。如果令人困惑,请提出问题。

© www.soinside.com 2019 - 2024. All rights reserved.