рельсы полукомплекс STI с моделью данных предков, планируя маршруты и контроллеры - PullRequest
7 голосов
/ 22 марта 2012

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

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

Country (has_many reviews & cities)
Subdivision/State (optional, sometimes it doesnt exist,  also reviewable, has_many cities) 
City (has places & review)
Burrow (optional, also reviewable ex: Brooklyn)
Neighborhood (optional & reviewable, ex: williamsburg)
Place (belongs to city)

Мне также интересно добавить больше сложности.Я также хочу время от времени включать подразделения ... то есть для США, я мог бы добавить Техас или для Германии, Баверия и иметь возможность их просмотра, но не в каждой стране есть регионы, и даже те, которые есть, никогда не будут проверяться.Так что это совсем не строго.Я бы хотел, чтобы это было как можно проще и гибче.

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

Я действительно не уверен, какой маршрут мне выбрать?Например, что произойдет, если у меня есть Страна и Город ... и затем я решу добавить Нору?

Могу ли я дать теги мест (т. Е. Вильямсбург, Бруклин) принадлежащие Нью-Йорку, а теги принадлежат Нью-Йорку?

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

Поэтому я ищу предложения для тех, кто сделал что-то связанное.

Использование Rails 3.2 и mongoid.

Ответы [ 4 ]

3 голосов
/ 30 марта 2012

Я построил что-то очень похожее и нашел два совершенно разных способа, которые оба работали хорошо.

Способ 1: Страна »Подгород» Город »Окрестности

Первый способ, который сработал для меня, это сделать это с помощью Country, Subcountry, City, Neighborhood. Это хорошо сопоставляется с основными службами геокодирования и является достаточным для большинства простых применений. Это может быть STI (как в вашем примере) или с несколькими таблицами (как я это сделал).

В вашем примере вы написали "Подразделение / Штат". Мои два цента - избегать использования этих терминов и использовать вместо этого «Subcountry», потому что это стандарт ISO, и вы избавите себя от некоторой путаницы, когда другой разработчик считает, что подразделение является крошечным соседством с домами, или когда у вас неамериканская страна который не использует штаты, а использует провинции.

Это то, на чем я остановился после многих экспериментов с попытками использовать названия моделей, таких как Регион, Район, Область, Зона и т. Д., И оставил их как слишком расплывчатые или слишком конкретные. В вашем случае ИППП может быть хорошо использовать больше имен.

Одним сюрпризом является то, что это большая помощь в написании ассоциаций, которые имеют многоуровневый характер, например, например, country.cities (пропуская субстрану). Это связано с тем, что иногда посреднической модели не существует (т. Е. Нет субстран). В вашей ИППП это может быть сложнее.

Кроме того, вы значительно ускоряетесь, если денормализуете свои таблицы, например, в моей таблице городов есть поле страны. Это делает обновление информации немного сложнее, но оно того стоит. Ваша ИППП может быть эквивалентна этому при использовании тегов.

Способ 2: зоны, представляющие собой списки фигур широты / ширины с ограничивающими прямоугольниками

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

В Postgres есть несколько хороших помощников для геокодирования, и вы можете ускорить поиск, выполнив ограничивающие рамки min / max lat / lng. Мы также сохранили данные, такие как ожидаемая центральная точка зоны (в которой мы будем указывать булавку на карте) и радиус (полезно для расчета запросов типа «показать мне все элементы х в пределах расстояния y).

При таком подходе мы смогли создать интересные зоны, такие как «Бродвей в Нью-Йорке», который на самом деле не столько соседство, сколько длинная улица, и «Бассейн Амазонки», который определяется рекой, а не страной.

2 голосов
/ 31 марта 2012

Модель STI с Ancestry и с Polymprphic Relation

Я построил что-то похожее для предыдущих проектов, и пошел на STI с родословной, потому что это очень гибкий и позволяет моделировать дерево узлов,Не все промежуточные узлы должны существовать (как в вашем примере State / Subdivision / Subcountry).

Для Mongoid существует как минимум два драгоценных камня предков: mongoid-ancestry и mongestry (ссылки ниже).

В качестве дополнительного преимущества использования STI с предками, вы также можете моделировать другие связанные с местоположением узлы, скажем, рестораны или другие места.

Вы также можете добавить информацию о географическом местоположении lat / lon ко всем вашим узлам, чтобы вы могли геотегировать их.В приведенном ниже примере я просто использовал один набор координат географического местоположения (центральная точка), но вы, конечно, можете также добавить несколько географических местоположений для моделирования ограничивающего прямоугольника.

Вы можете расположить узлы в любом порядке.Вам нравится, например, через this_node.children.create(...).При использовании MySQL с предками, вы можете передать тип только что созданного узла.Должен быть аналогичный способ с mongoid-ancestry (еще не пробовал).

В дополнение к древовидным узлам, вы можете использовать полиморфную коллекцию для моделирования Обзоров, а также Теги (ну,есть жемчужина для activ_as_taggable, поэтому вам не нужно самим моделировать теги).

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

Эту парадигму можно использовать с хранилищами данных Mongoid или SQL.

# app/models/geo_node.rb

class GeoNode  # this is the parent class; geo_nodes is the table name / collection name.
  include Mongoid::Document
  has_ancestry   # either this
  has_mongestry  # or this
  has_many :reviews, :as => :reviewable

  field :lat, type: Float
  field :lon, type: Float

  field :name, type: String
  field :desc, type: String
  # ...
end

# app/models/geo_node/country.rb
class Country < GeoNode
end

# app/models/geo_node/subcountry.rb
Class Subcountry < GeoNode
end

# app/models/geo_node/city.rb
class City < GeoNode
end


# app/models/review.rb
class Review
  include Mongoid::Document
  belongs_to :reviewable, :polymorphic => true
  field :title
  field :details
end

Проверьте следующие ссылки:

Большое спасибо Stefan Kroes за его удивительный камень предков, иАнтону Орлу за адаптацию его к монгоиду (монгоидно-родословная).родословная является одним из самых полезных драгоценных камней, которые я видел.

1 голос
/ 27 марта 2012

Я бы, вероятно, сохранял свою модель данных очень неограниченно и обрабатывал бы любые особенности, связанные с тем, какие фильтры отображать в контроллере / представлении.Создайте таблицу сопоставления, в которой вы можете сопоставить атрибуты (например, город, район, штат, страну) полиморфно, а также полиморфно с reviewable.

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

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

Содержите схему БД в чистоте и бизнес-логику в рубине.

1 голос
/ 22 марта 2012

Звучит как хороший кандидат для вложенных маршрутов / ресурсов.В routes.rb сделайте что-то вроде:

resources :cities do
  resources :reviews
end

resources :countries do
  resources :reviews
end

resources :places do
  resources :reviews
end

, что должно произвести что-то вроде rake routes:

   reviews_cities GET /cities/:id/reviews     {:controller=>"reviews", :action=>"index"}
reviews_countries GET /countries/:id/reviews  {:controller=>"reviews", :action=>"index"}
   reviews_places GET /countries/:id/reviews  {:controller=>"reviews", :action=>"index"}

...etc., etc.

В действии контроллера вы ищете совпадение с :id из reviewable записи и отправлять только отзывы, которые прикреплены к этому reviewable объекту.

Также см. Раздел nested resources Руководство по маршрутизации Rails , и это RailsCast по полиморфным отношениям , в котором есть краткий раздел о маршрутизации и о том, как все выстроить правильно.

...