Сортировка / группировка записей для разбивки на страницы со многими ограничениями на основе ассоциаций

Я работаю над приложением, которое действует как каталог мест и (в настоящее время, но, возможно, не всегда) позволяет фильтровать объекты определенного предприятия по 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)

Другие вопросы по тегам