рельсовое происхождение - PullRequest
5 голосов
/ 07 июня 2011

Я только что следовал учебнику по 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

Вот как я хочу отобразить листинг, но я также хочу разбить это отсортированное дерево на страницы.

1 Ответ

4 голосов
/ 29 июня 2011

Возможно, но мне удалось сделать это только за две поездки в базу данных.

Основная проблема связана с невозможностью установить ограничения для дочерних узлов, что приводит либо к усечению дочерних узлов, либо к потере сирот на последующих страницах.

Пример:

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 (которая has_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

Некоторые заметки:

  • MySQL перестанет использовать индекс столбца предков, если будет использоваться несколько операторов LIKE. FORCE INDEX заставляет mySQL использовать индекс и предотвращает полное сканирование таблицы
  • Операторы LIKE создаются только для узлов с прямыми дочерними элементами, поэтому столбец replies_count пригодится
  • Метод класса добавляет дочерних элементов в root, который представляет собой WillPaginate :: Collection

Наконец, вы можете управлять ими:

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

Ключевой метод здесь - range_nodes , который смешивается с плагином предков и с вашей моделью. Это в основном берет отсортированный массив узлов и возвращает отсортированный и иерархический хэш.

Я ценю, что этот метод напрямую не отвечает на ваш вопрос, но я надеюсь, что тот же метод, с настройками, может быть применен для вашего случая.

Вероятно, есть более элегантный способ сделать это, но в целом я доволен решением (пока не появится лучший).

...