#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, который не будет отображаться корректно, поскольку его родители отсутствуют.
Одно из решений, которое я использовал, — это использование двух запросов:
- Первый возвращает только корневые узлы. Они могут быть отсортированы, и именно этот запрос разбивается на страницы.
- Выдается второй запрос, основанный на первом, который возвращает все дочерние элементы разбитых на страницы родителей. Вы должны иметь возможность сортировать дочерних элементов по уровням.
В моем случае у меня есть модель 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. Пожалуйста, укажите проблему, если это сбивает с толку.