разбивка на страницы родословной rails

#ruby-on-rails-3 #tree #will-paginate #pagination

#ruby-on-rails-3 #дерево #будет разбивать на страницы #разбивка на страницы

Вопрос:

Я только что следовал руководству Railscast:

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

Возможно ли разбивать на страницы результаты из родословной, которые были упорядочены? например: Учитывая, что у меня есть следующее в моем контроллере сообщений:

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

Тогда как бы я разбил это на страницы, поскольку это приведет к хэшу?

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

 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:

Это возможно, но мне удалось сделать это только с помощью двух обращений к базе данных.

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

Пример:

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

ОГРАНИЧЕНИЕ 0,3 (для примера выше) вернет первые три записи, которые будут отображать все, кроме id: 119. Последующее ОГРАНИЧЕНИЕ 3,3 вернет идентификатор: 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
  

Некоторые примечания:

  • MySQL перестанет использовать индекс столбца родословной, если используются несколько операторов LIKE. ПРИНУДИТЕЛЬНЫЙ ИНДЕКС заставляет MySQL использовать индекс и предотвращает полное сканирование таблицы
  • Инструкции LIKE создаются только для узлов с прямыми дочерними элементами, так что столбец replie_count пришелся кстати
  • Что делает метод класса, так это добавляет дочерние элементы в root, который является WillPaginate::Collection

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

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

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

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

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

Ответ №2:

Сначала:

Предполагая, что вы сортируете по дате создания, вы можете отсортировать по id . Это работает в подавляющем большинстве всех случаев.

Сортировка:

Если вы отсортируете по ancestry (столбец материализованного пути), то сначала вы получите все корневые узлы, а затем дочерние, возможно, сотни записей позже.

Итак, вы хотите выполнить сортировку по родословной и идентификатору вместе:

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

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

Ruby:

 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 для столбца.

Я попытался сделать информацию о сопоставлении более понятной в ancestry gem. Пожалуйста, укажите проблему, если это сбивает с толку.