5

用户可以在组中。并且为项目/产品分配了可以看到该项目的组。如果用户在分配的组之一中,他们可以看到该项目。

我既不希望公共(无组中的匿名用户)也不希望无组用户(登录的用户不属于任何组)看到该项目。但我希望界面允许为项目分配“所有/任何组”属性,以便任何组中的用户都可以看到该项目。

我应该在哪里/如何存储这个作业?

ps 我希望该技术也可以扩展到其他实体,例如我将文件分配给类别,并且组链接到类别。因此,当“所有/任何类别”将文件标记为可见时,如果用户(通过组和组类别)链接到至少一个类别,则该文件对他们可见。

决定:

似乎选择是作为实体组表中的行还是实体表中的字段来实现。选择的答案使用前者。

并且要么管理表中的组成员身份,要么添加 JOIN 条件。选择的答案使用了前者,但我将使用后者。我在查询和使用之间建立了间接关系,因此如果(何时)性能出现问题,我应该能够(如建议的那样)更改为下面的托管表,而无需更改使用。

我还有其他特殊组,例如“管理员”、“用户”等,它们也可以更容易地融入相同的概念(基础只是组列表),而不是每个实体的特殊和可变字段处理。

谢谢大家。

4

4 回答 4

2

我会把它items作为boolean/bit列放在表中IsVisibleToAllGroups

它确实使为用户获取所有项目的查询不那么简单,但另一种选择是扩展“所有组”,因此您为每个单独的组添加一个权限行,但这可能会导致数量的巨大扩展行,如果稍后添加了一个额外的组,您仍然必须保持最新,并且以某种方式区分明确授予所有(当前和未来)组的权限和恰好授予所有组的权限目前存在。

编辑您没有提及您正在使用的 RDBMS。您可以采取的另一种方法是设置组层次结构。

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  
于 2011-10-10T09:17:41.917 回答
1

正如 Martin Smith 和 Mikael Eriksson 所提到的,将其作为实体的属性是一种非常简洁和直接的方法。纯粹就数据表示而言,这给人一种非常好的感觉。

但是,我也会考虑您可能对数据进行的查询。例如,根据您的描述,您的查询似乎最有可能从单个用户开始,找到他们所属的组,然后找到与他们关联的实体。可能是这样的...

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. 创建一个全球组
  2. 如果一个实体对所有组可见,则将它们映射到 GLOBAL 组
  3. 如果将用户添加到组,请确保他们也链接到 GLOBAL 组
  4. 如果用户从所有组中删除,请确保他们也从 GLOBAL 组中删除

这样,原始的简单查询无需修改即可工作。这意味着不需要 UNION,它有排序和重复数据删除的开销,也不需要 isVisibleToAllGroups 上的 INDEX。相反,开销转移到维护用户链接到哪些组;而是一次性开销。

这假设“我可以看到哪些实体”这个问题比更改组更常见。它还添加了由 DATA 而不是由 SCHEMA 定义的行为,这需要良好的文档和理解。因此,我确实将其视为一种强大的优化类型,但我也将其视为一种需要在数据库设计中考虑的交易和平衡类型的妥协。

于 2011-10-10T09:44:17.493 回答
0

我将添加一个包含项目所需组的名称(或编号)的列“needs_group”,而不是在每个查询中都需要额外逻辑的布尔值。NULL 字段是否意味着“无人”或“所有人”只是一个(允许/拒绝)设计决策。创建一个“公共”组并将每个人都放入其中也是一项设计决策。YMMV

于 2011-10-10T10:19:52.390 回答
0

这个概念应该让你前进:

在此处输入图像描述

如果满足以下条件,用户可以看到产品:

  • USER_GROUP_PRODUCT 中存在相应的行
  • 或 PRODUCT.PUBLIC 为 TRUE(如果我正确理解您的问题,用户至少属于一个组)。

关于这个模型有两个关键点需要考虑:

  1. 识别关系的自由使用- 父母的主键在孩子的主键中“迁移”,这使得 GROUP_ID 在底部 USER_GROUP_PRODUCT 的“合并”。这就是允许 DBMS 强制执行约束,即用户和产品必须属于同一组才能相互可见。使用非标识关系和代理键会阻止 DBMS 直接执行(您必须编写自定义触发器)。
  2. PRODUCT.PUBLIC 的用法 - 您必须在客户端代码中将此字段视为“魔术”。另一种方法是简单地用所有可能的组合填充 USER_GROUP_PRODUCT,但是这种方法在添加新用户的情况下很脆弱 - 除非您也更新 USER_GROUP_PRODUCT,否则它不会自动看到产品,但是您怎么知道您需要除非您有 PRODUCT.PUBLIC 之类的字段,否则请更新它?所以如果你无论如何都不能避免 PRODUCT.PUBLIC,为什么不特别对待它并在数据库中节省一些存储空间呢?
于 2011-10-11T02:12:57.853 回答