Как мне записать в базу данных, что товар / товар виден «всем» группам? - PullRequest
5 голосов
/ 10 октября 2011

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

Я не хочу, чтобы этот элемент был доступен ни общедоступным (анонимные пользователи ни в одной группе), ни групповым пользователям (вошедшим в систему ни в каких группах).Но я хочу, чтобы интерфейс позволял назначать элементу атрибут «все / любые группы», чтобы пользователи, которые находятся в любой группе вообще, могли видеть элемент.

Где / Как мне сохранить это назначение?

ps Я ожидаю, что техника будет распространена и на другие объекты, например, я бы назначил файл категории, а группы были связаны с категориями.поэтому, когда файл помечен как видимый в категории «все / любая», тогда, если пользователь (через группы и групповые категории) связан хотя бы с одной категорией, файл будет им виден.

Решение:

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

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

У меня есть другие специальныетакие группы, как «администратор», «пользователи» и т. д., которые также могут вписаться в ту же концепцию (основой которой является просто список групп) легче, чем специальная и переменная обработка полей для каждого объекта.

спасибо всем.

Ответы [ 4 ]

2 голосов
/ 10 октября 2011

Я бы поместил его в таблицу items как столбец boolean / bit IsVisibleToAllGroups.

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

Редактировать Вы не упоминаете используемую СУБД. Еще один подход, который вы можете использовать, - это иерархия групп.

GroupId     ParentGroupId Name
----------- ------------- ----------
0           NULL          Base Group
1           0             Group 1
2           0             Group 2

Затем вы можете назначить свои «все» разрешения для GroupId=0 и использовать (подход SQL Server ниже)

WITH GroupsForUser
     AS (SELECT G.GroupId,
                G.ParentGroupId
         FROM   UserGroups UG
                JOIN Groups G
                  ON G.GroupId = UG.GroupId
         WHERE  UserId = @UserId
         UNION ALL
         SELECT G.GroupId,
                G.ParentGroupId
         FROM   Groups G
                JOIN GroupsForUser GU
                  ON G.GroupId = GU.ParentGroupId)
SELECT IG.ItemId
FROM   GroupsForUser GU
       JOIN ItemGroups IG
         ON IG.GroupId = GU.GroupId  
1 голос
/ 10 октября 2011

Как уже упоминалось Мартином Смитом и Микаэлем Эрикссоном, сделать это свойство объекта очень аккуратным и понятным подходом.Чисто с точки зрения представления данных, это очень приятно.

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

SELECT DISTINCT -- If both user and entity relate to multiple groups, de-dupe them
  entity.*
FROM
  user
INNER JOIN
  user_link_group
    ON user.id = user_link_group.user_id
INNER JOIN
  group_link_entity
    ON group_link_entity.group_id = user_link_group.group_id
INNER JOIN
  entity
    ON entity.id = group_link_entity.entity_id
WHERE
  user.id = @user_id

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

<ORIGINAL QUERY>

UNION       -- Not UNION ALL, as the next query may duplicate results from above

SELECT
  entity.*
FROM
  entity
WHERE
  EXISTS (SELECT * FROM user_link_group WHERE user_id = @user_id)
  AND isVisibleToAllGroups != 0

-- NOTE: This also implies the need for an additional index on [isVisibleToAllGroups]

Вместо создания углового регистра в запросе "какой объект можно увидеть", вместо этого можно создать угловой регистр при ведении ссылкитаблицы ...

  1. Создание группы GLOBAL
  2. Если элемент виден всем группам, сопоставьте их с группой GLOBAL
  3. Если пользователь добавлен вгруппа, убедитесь, что они также связаны с группой GLOBAL
  4. Если пользователь удален из всех групп, убедитесь, что они также удалены из группы GLOBAL

Таким образом,Оригинальный простой запрос работает без изменений.Это означает, что никакой UNION не требуется, с его накладными расходами на сортировку и дедупликацию, и не требуется ни INDEX для isVisibleToAllGroups.Вместо этого накладные расходы переносятся на поддержание того, с какими группами связан пользователь;вместо этого один раз.

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

0 голосов
/ 11 октября 2011

Эта концепция должна помочь вам:

enter image description here

Пользователь может увидеть товар, если:

  • соответствующая строка существует в USER_GROUP_PRODUCT
  • или PRODUCT.PUBLIC - ИСТИНА (и пользователь входит хотя бы в одну группу, если я правильно понимаю ваш вопрос).

Есть два ключевых момента, которые следует учитывать в этой модели:

  1. Либеральное использование идентификации отношений - первичные ключи родителей «переносятся» в первичные ключи потомков, что позволяет «объединять» GROUP_ID в нижней части USER_GROUP_PRODUCT. Это то, что позволяет СУБД применять ограничение, согласно которому и пользователь, и продукт должны принадлежать к одной и той же группе 1020 *, чтобы быть взаимно видимыми. Использование неидентифицирующих отношений и суррогатных ключей не позволит СУБД реализовать это напрямую (вам придется написать собственные триггеры).
  2. Использование PRODUCT.PUBLIC - вам придется рассматривать это поле как «магическое» в вашем клиентском коде. Альтернатива - просто заполнить USER_GROUP_PRODUCT всеми возможными комбинациями, но этот подход хрупок в случае добавления нового пользователя - он не будет автоматически видеть продукт, если вы также не обновите USER_GROUP_PRODUCT, но откуда вы знаете, что вам нужно обновить его, если у вас нет поля, такого как PRODUCT.PUBLIC? Так что, если вы все равно не можете избежать PRODUCT.PUBLIC, почему бы не обработать его специально и не сэкономить место в базе данных?
0 голосов
/ 10 октября 2011

Вместо логического значения, которому нужна дополнительная логика в каждом запросе, я бы добавил столбец needs_group, содержащий имя (или номер) группы, которая требуется для элемента. То, что поле NULL означает «никто» или «все», - это только (разрешить / запретить) проектное решение. Создание одной «публичной» группы и включение всех в нее также является дизайнерским решением. YMMV

...