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

Я работаю над приложением, которое действует как каталог мест и (в настоящее время, но, возможно, не всегда) позволяет фильтровать объекты определенного предприятия по 1 городу, 1 району и 1 категории. Категории могут быть вложенными, и поэтому они имеют parent_id. Ассоциации можно понять, посмотрев код модели ниже (все довольно просто). Пока что все идет гладко, но остался один помеха.

Сначала несколько важных замечаний, которые повлияют на ответы.

1. Я использую will_paginate для разбиения на страницы в ассоциации мест. При этом я не могу разбить на страницы в массиве. Ну, я мог бы , но производительность была бы проблемой внизу.

2. Я использую модель тегов в качестве объекта связи между бизнесом и связанными с ним категориями. В настоящее время интерфейс настроен только для того, чтобы разрешить привязку одной категории к какой-либо конкретной компании, и должен быть присоединен хотя бы один . Я планирую позже расширить интерфейс для общедоступных страниц, чтобы разрешить фильтрацию по нескольким категориям, поэтому нельзя вносить изменения в эту часть структуры приложения, если не существует намного, гораздо лучшего способа выполнения этого .

3. Возможное решение, которое могло бы (мы надеемся) существовать - это псевдоним объединения таблиц в Arel или области действия, позволяющей категориям самостоятельно присоединяться к себе, чтобы получить к родителю. В настоящее время я бы счел это приемлемым, если бы в решении предполагалось, что будет не более 1 уровня вложенности. Однако эта жертва является последним средством, поскольку она действительно ослабит функциональность, которую я хочу представить в будущем.

4. В связи с последним пунктом я рассмотрел will_paginate/array, но был напуган их отказом от ответственности («Если вы знаете, что делаете, и действительно должны разбивать массивы на части, эта функция явно "). Вспоминая ночные кошмары о производительности, с которыми я сталкивался в прошлом, когда пытался развернуть свой собственный плагин для разбивки на страницы, я бы хотел этого избежать, если кто-то не сможет адекватно объяснить мне последствия для производительности такого решения.

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

Теперь вопрос, который я хочу задать.

Справочная информация : для некоторых спецификаций дизайна общедоступных представлений требуется, чтобы все объекты текущего города отображались в списке и группировались по родительской категории (не нужно отображать подкатегории, но все объекты теги, чьи предприятия принадлежат подкатегории, также должны быть сгруппированы с местами, теги которых принадлежат родителю). Затем эти объекты сортируются по родительской категории, а затем сортируются по названию предприятия, которому принадлежит объект. Это должна быть плоская ассоциация, которая затем подается в will_paginate. Это обрабатывает запрос ActiveRecord, который в дальнейшем я буду называть @venues, затем выгружается в JSON, кэшируется и выводится на страницу.

Вопрос : Как мне построить @venues с указанной группировкой / упорядочением, чтобы эти места можно было правильно разбить на страницы и отображать в соответствии со спецификациями этого интерфейса?

приложение / модели / 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

приложение / модели / venue.rb

# Fields: id, business_id, city_id, neighborhood_id, ...
class Venue < ActiveRecord::Base
  belongs_to :business
  belongs_to :city
  belongs_to :neighborhood
end

приложение / модели / tag.rb

# Fields: id, category_id, business_id, ...
class Tag < ActiveRecord::Base
  belongs_to :business
  belongs_to :category
  belongs_to :venue
end

приложение / модели / category.rb

# Fields: id, parent_id, name, description, ...
class Category < ActiveRecord::Base
  has_many :tags, :dependent => :destroy
end

приложение / модели / city.rb

# Fields: id, name, description, ...
class City < ActiveRecord::Base
  has_many :neighborhoods, :dependent => :destroy
  has_many :venues
end

приложение / модели / neighborhood.rb

# Fields: id, city_id, name, description
class Neighborhood < ActiveRecord::Base
  belongs_to :city
  has_many :venues
  has_many :businesses, :through => :venues
end

Это был один дурак, чтобы объяснить, и я могу предоставить больше информации, если это будет необходимо.

P.S. Использование Rails 3.0.9 для этого приложения с MySQL.

P.S.S. Мне также интересны шаблоны или гемы, которые упрощают этот вид фильтрации на основе возможных значений полей нескольких вложенных ассоциаций. Я предоставлю upvote + accept + bounty тому, кто сможет предоставить точное решение, используя драгоценный камень модели вложенного набора, такой как Awesome Nested Set, для группировки / сортировки / разбиения на страницы таким образом.

1 Ответ

3 голосов
/ 12 октября 2011

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

, как сказал Ксавьев комментарии вы должны использовать модель вложенного множества.Я использую это:

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)

...