Попытка избежать "Полиморфных Ассоциаций" и поддержать ссылочную целостность внешнего ключа - PullRequest
4 голосов
/ 30 августа 2011

Я создаю сайт, похожий на Yelp (механизм рекомендаций, но в меньшем масштабе), поэтому в системе будет три основных объекта: Пользователь, Место (включая предприятия) и Событие.

Теперь меня интересует, как хранить информацию, такую ​​как фотографии, комментарии и «комплименты» (похожие на «Мне нравится» в Facebook) для каждого из этих типов объектов, а также для каждого объекта, к которому они могут быть применены (например, комментарий к рекомендации, фото и т. д.).Прямо сейчас, как я это делал, это была отдельная таблица для каждого, то есть

Фотография (id, type , owner_id , is_main и т. Д.))
где тип представляет: 1 = пользователь, 2 = место, 3 = событие

Комментарий (id, object_type, object_id , user_id, content и т. Д.и т. д.)
, где object_type может быть несколькими различными объектами, такими как фотографии, рекомендации и т. д.

Compliment ( object_id , object_type , compment_type, user_id)
где object_type может быть несколькими различными объектами, такими как фотографии, рекомендации и т. Д.

Activity (id, source, source_type , source_id и т. Д.) //for "activity feed"
где source_type - это пользователь, место или событие

Уведомление (идентификатор, получатель, отправитель, тип_операции, object_type , object_id и т. Д.)
, где object_type & object_id будет использоваться для предоставления прямой ссылки на объект уведомленияНапример, на фотографии пользователя, которая была отмечена

Но после чтения нескольких сообщений на SO, я понял, что яне может поддерживать ссылочную целостность с внешним ключом, поскольку для этого требуется отношение 1: 1, а мои поля source_id / object_id могут относиться к идентификатору в нескольких таблицах.Поэтому я решил использовать метод сохранения основного объекта, но затем разбить его на подмножества, например

User_Photo (photo_id, user_id) |Place_Photo (photo_id, place_id) |и т.д ...

Photo_Comment (comment_id, photo_id) |Рекомендация_Комментарий (comment_id, rec_id) |и т. д. *

Комплимент (id, ...) //would need to add a surrogate key to Compliment table now

Photo_Compliment (compment_id, photo_id) |Comment_Compliment (идентификатор_комментария, идентификатор_комментария) |и т. д. *

User_Activity (Activity_id, user_id) |Place_Activity (Activity_id, Place_id) |и т.д. ...

Я думал, что могу просто создать представления, соединяющие каждую вложенную таблицу с основной таблицей, чтобы получить желаемые результаты.Кроме того, я думаю, что это будет соответствовать моим объектным моделям в Code Igniter.

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

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

Единственная «проблема», которую я вижу, состоит в том, что у меня будет много таблиц (так как сейчас у меня около 72, так что, думаю, я получу чуть менее 90 таблиц послеЯ добавляю дополнения), и, насколько я могу судить, это не проблема.

Очень благодарен за любые отзывы.Заранее спасибо.

РЕДАКТИРОВАТЬ : Просто чтобы быть ясным, я не беспокоюсь, если я в конечном итоге с еще 10 или около того таблиц.Из того, что я знаю, количество таблиц не является большой проблемой (если они используются) ... если вы не сказали 200 или около того: /

Ответы [ 3 ]

6 голосов
/ 31 августа 2011

Некоторые предложения для этого UoD (вселенной дискурса)

  • Пользователь с именем Bob вошел в систему.
  • Пользователь по имени Боб загрузил фото № 56.
  • Есть место под названием Лондон.
  • Фотография № 56 - место под названием Лондон.
  • Пользователь по имени Джо создал комментарий "очень приятно" на фото № 56.

Для ввода идентификаторов объектов

  • Пользователь (UserID) вошел в систему.
  • Пользователь (UserID) загрузил фотографию (PhotoID).
  • Есть место (PlaceID).
  • Фото (PhotoID) имеет место (PlaceID).
  • Пользователь (UserID) создал комментарий (CommentID) к фотографии (PhotoID).

Типы фактов

  • Пользователь вошел в систему.
  • Пользователь загрузил фотографию.
  • Место существует.
  • Фотография места.
  • Пользователь создал комментарий к фотографии.

Теперь извлечь предикатов

Predicate               Predicate Arity
---------------------------------------------
... logged in            1 (Unary predicate)
... uploaded ...         2 (Binary)
... exists               1 (Unary) 
... is of ...            2 (Binary)
... created ... on ...   3 (Ternary)

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

enter image description here

Роль предиката (Role_1_ID, Role_2_ID, Role_3_ID) - это роль, которую объект играет в предикате. Замените ... в предикате слева направо каждым Role_ID. Обратите внимание, что только Role_1_ID является обязательным (по крайней мере, унарный предикат), остальные два могут быть NULL.

В этой простой модели можно предложить что угодно. Следовательно, вам нужно будет реализовать ограничения на прикладном уровне . Например, вы должны убедиться, что можно создать Comment на Place, но не создавать Place на Place. Не все предикаты представляют действие, например, ... logged in - это действие, а ... is of ... - нет. Итак, ваш фид активности будет перечислять все Propositions с Predicate.IsAction = True.

3 голосов
/ 31 августа 2011

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

Способ решить эту проблему - убедиться, что объекты, которые могут привлекать комментарии и комплименты, являются логическими подтипами одного супертипа. С логической точки зрения это означает, что у вас есть сущность «THING_OF_INTEREST» (я не даю здесь рекомендацию по соглашению об именах!), И каждая из различных конкретных вещей, которые привлекают комментарии и комплименты, будет подчинена тип THING_OF_INTEREST. Поэтому ваша таблица комментариев будет иметь столбец FK "thing_of_interest_id" и аналогично для вашей таблицы комплиментов. У вас все еще будут таблицы подтипов, но они будут иметь 1: 1 FK с THING_OF_INTEREST. Другими словами, THING_OF_INTEREST выполняет задачу предоставления вам одного домена первичного ключа, тогда как все таблицы подтипов содержат атрибуты, специфичные для типа. Таким образом, вы все равно можете использовать декларативную ссылочную целостность для обеспечения соблюдения ваших комментариев и связей комплимента, не имея отдельных таблиц для разных типов комментариев и комплиментов.

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

В зависимости от того, как вы поступите после своих комментариев и рекомендаций, вам, вероятно, потребуется (но не обязательно) физически реализовать THING_OF_INTEREST - который будет иметь как минимум два атрибута: первичный ключ (обычно int) и атрибут разделения, который сообщает вы какой подтип вещи это.

2 голосов
/ 31 августа 2011

Если вам нужно referential integrity (RI), нет лучшего способа сделать это, чем использовать соединительные таблицы «многие ко многим». Да, у вас в системе много таблиц, но это та цена, которую вам нужно заплатить. Он также имеет некоторые другие преимущества в этом направлении, например, вы получаете какое-то разделение бесплатно: вы получаете данные, разделенные по типу отношения, каждое в своей таблице. Это предлагает RI, но это не на 100% безопасно, например, нет ничего, что могло бы гарантировать вам, что комментарий относится к фотографии и только к этой фотографии, вам нужно будет принудительно ввести такие ограничения вручную, если они вам понадобятся.

С другой стороны, использование универсального решения, как вы уже сделали, ускоряет процесс, и его легче расширять в будущем, но не будет никакого RI, если вы не закодируете его вручную (что очень сложный и намного сложнее иметь дело с альтернативой M: M для каждого типа отношений).

Просто чтобы упомянуть другую альтернативу, аналогичную вашей существующей реализации, вы можете использовать пользовательскую таблицу соединений M: M для обработки всех ваших отношений независимо от их типа: object1_type, object1_id, object2_type, object2_id. Простой, но не имеющий других преимуществ, кроме очень простой в реализации и расширении. Я бы порекомендовал это, только если вам не нужен RI, и вы получили много таблиц, связанных между собой.

...