Сортировка / группировка записей для разбивки на страницы со многими ограничениями на основе ассоциаций
Я работаю над приложением, которое действует как каталог мест и (в настоящее время, но, возможно, не всегда) позволяет фильтровать объекты определенного предприятия по 1 городу, 1 району и 1 категории. Категории могут быть вложенными, и поэтому они имеют parent_id. Ассоциации можно понять, посмотрев код модели ниже (все довольно просто). Пока что все идет гладко, но осталась одна преграда.
Сначала несколько важных замечаний, которые повлияют на ответы.
1. Я использую will_paginate для разбиения на страницы в ассоциации мест. При этом я не могу разбить на страницы в массиве. Ну, я мог бы, но производительность будет проблемой в будущем.
2. Я использую модель тегов в качестве объекта связи между бизнесом и связанными с ним категориями. В настоящее время интерфейс настроен только на то, чтобы разрешить привязку одной категории к любому конкретному бизнесу, и как минимум одна должна быть присоединена. Я планирую позже расширить интерфейс для общедоступных страниц, чтобы разрешить фильтрацию по нескольким категориям, поэтому нельзя вносить изменения в эту часть структуры приложения, если для этого не существует гораздо более эффективного способа.
3. Возможное решение, которое могло бы (мы надеемся) существовать, - это наложение псевдонима таблицы в Arel или области действия, позволяющей категориям самостоятельно присоединяться к себе, чтобы получить к родителю. В настоящее время я бы счел это приемлемым, если бы в решении было сделано предположение о том, что будет не более 1 уровня вложенности. Однако эта жертва является последним средством, поскольку она действительно ослабит функциональность, которую я хочу представить в будущем.
4. В связи с последним пунктом, я посмотрел на will_paginate/array
, но был напуган их отказом от ответственности ("Если вы знаете, что делаете, и действительно должны разбивать массивы на части, потребуйте эту функцию явно"). Вспоминая ночные кошмары о производительности, с которыми я сталкивался в прошлом, когда пытался развернуть свой собственный плагин для разбивки на страницы, я бы хотел этого избежать, если только кто-нибудь не сможет адекватно объяснить мне последствия для производительности такого решения.
5. Этот сайт должен быть в состоянии принять обстрел. В настоящее время все кэшируется, и попадания в базу данных, не связанные с кэшем, относительно редки. Так и должно быть.
Теперь вопрос, который я хочу задать.
Справочная информация. Для некоторых спецификаций дизайна общедоступных представлений требуется, чтобы все объекты текущего города отображались в списке и группировались по родительской категории (не нужно отображать подкатегории, но все объекты, теги предприятий которых относятся к подкатегории, должны быть также сгруппированы с местами, чьи теги принадлежат родителю). Эти объекты затем сортируются по родительской категории, а затем сортируются по названию предприятия, которому принадлежит место. Это должна быть плоская ассоциация, которая затем подается в will_paginate. Это вызывает запрос ActiveRecord, который в дальнейшем будет называться @venues
, затем выгружается в JSON, кэшируется и отображается на странице.
Вопрос: Как мне построить @venues
с указанным группированием / упорядочением, чтобы эти места можно было правильно разбить на страницы и отображать в соответствии со спецификациями этого интерфейса?
app / models / business.rb:
# Fields: id, name, description, keywords, ...
class Business < ActiveRecord::Base
has_many :tags, :dependent => :destroy
has_many :categories, :through => :tags
has_many :venues, :dependent => :destroy
has_many :cities, :through => :venues
has_many :neighborhoods, :through => :venues
end
app / models / venue.rb:
# Fields: id, business_id, city_id, neighborhood_id, ...
class Venue < ActiveRecord::Base
belongs_to :business
belongs_to :city
belongs_to :neighborhood
end
app / models / tag.rb:
# Fields: id, category_id, business_id, ...
class Tag < ActiveRecord::Base
belongs_to :business
belongs_to :category
belongs_to :venue
end
app / models / category.rb:
# Fields: id, parent_id, name, description, ...
class Category < ActiveRecord::Base
has_many :tags, :dependent => :destroy
end
app / models / city.rb:
# Fields: id, name, description, ...
class City < ActiveRecord::Base
has_many :neighborhoods, :dependent => :destroy
has_many :venues
end
app / models / окрестности.rb:
# Fields: id, city_id, name, description
class Neighborhood < ActiveRecord::Base
belongs_to :city
has_many :venues
has_many :businesses, :through => :venues
end
Это был один дурак, чтобы объяснить, и я могу предоставить больше информации, если это будет необходимо.
PS Использование Rails 3.0.9 для этого приложения с MySQL.
PSS Меня также интересуют шаблоны или гемы, которые упрощают этот вид фильтрации на основе возможных значений полей нескольких вложенных ассоциаций. Я предоставлю upvote+accept+bounty тому, кто сможет предоставить точное решение, используя драгоценный камень модели Nested, такой как Awesome Nested Set, для группировки / сортировки / разбиения на страницы таким образом.
1 ответ
никакие подкатегории не должны отображаться, но все места, чьи теги компаний принадлежат подкатегории, также должны быть сгруппированы с местами, чьи теги принадлежат родителю.
как сказал Ксавье в комментарии, вы должны использовать модель вложенного множества. Я использую это:
https://github.com/collectiveidea/awesome_nested_set
И это дает мне возможность разбивать на страницы и сортировать как обычно:
module Category
extend ActiveSupport::Concern
included do
belongs_to :category
scope :sorted, includes(:category).order("categories.lft, #{table_name}.position")
end
module ClassMethods
def tree(category=nil)
return scoped unless category
scoped.includes(:category).where([
"categories.lft BETWEEN ? AND ?",category.lft, category.rgt
])
end
end # ClassMethods
end
#then with some category
@animals = Animal.tree(@mamal_category)
это даст вам всех травоядных, плотоядных животных, всеядных животных и т. д. и т. д. и подкатегории в одном вызове sql, и это легко разбирается в страницах, сортируется, так как это область применения.
Также смотрите: получить все товары категории и дочерних категорий (rails, awesome_nested_set)