【问题标题】:Postgres conditional aggregate arrayPostgres 条件聚合数组
【发布时间】:2019-08-08 11:41:30
【问题描述】:

我有一个包含应用程序功能的自引用树结构。通过另外两个多对多表,我想知道用户可以访问哪些功能。

app_user (id, name, is_superuser)
feature (id, parent_id, name)
job (id, name)
job_feature_relationship (job_id, feature_id, deny)
job_user_relationship (job_id, user_id)

总结:用户可以有多个工作。作业包含一组功能,可以选择拒绝。这对于在树结构中管理权限很有用,例如:

有以下特点:

sales

sales/product
sales/product/createProduct
sales/product/deleteProduct
sales/product/listProducts

sales/customer
sales/customer/createCustomer
sales/customer/deleteCustomer
sales/customer/listCustomers

只有叶节点(createProductdeleteProductlistProductscreateCustomerdeleteCustomerlistCustomers)是实际操作。

如果工作看起来像这样:

(sales)

用户应该能够执行销售下的所有操作,因此在此示例中所有操作。

但是,如果工作看起来像这样:

(sales/customer)

(sales/customer/deleteCustomer, deny)

用户应该只能listCustomerscreateCustomer,因为sales/customer 将允许子树中的所有内容,但deleteCustomer 被拒绝。

最后一个补充,与此问题不完全相关,但superuser 用户可以访问所有功能,这意味着如果 if_superuser bool 标志设置为 true,则无论作业如何,所有功能都应该可以访问。

我离解决方案只有一步之遥。

with recursive
feature_tree(parent, child, name, level) as (
    select id, id, feature, 1
    from feature
    union all
    select parent, id, feature.feature, feature_tree.level + 1
    from feature_tree
    join feature on (
        feature_tree.child = feature.parent_id
    )
)
select
    feature_tree.name
    , feature_tree.level
    , deny
from feature
join feature_tree on (
    feature_tree.parent = feature.id
)
left join job_feature_relationship on (
    job_feature_relationship.feature_id = feature.id
)
left join job_user_relationship on (
    job_user_relationship.job_id = job_feature_relationship.job_id
)
join app_user on (
    -- app_user.id = 1
    app_user.id = 2
    -- app_user.id = 3
    -- app_user.id = 4
)
where (
    -- this condition is responsible for only returning leafs
    not exists(select 1 from feature descendant where descendant.parent_id = feature_tree.child)
    and (
        app_user.is_superuser
        or job_user_relationship.user_id = app_user.id
    )
)
order by feature_tree.level desc;

SEE THIS FIDDLE

我想要的可以很容易地依次描述,python示例代码:

features = set()
for feature in features:
    if feature.deny:
        features = features.difference(feature.tree)
    else:
        features = features.union(feature.tree)

由于排序,添加和删除一组特征将具有正确的最终结果。 提前感谢您的帮助。

【问题讨论】:

  • 请给我们一组我们可以使用的样本数据。这非常复杂,而且很难生成自己的测试数据。
  • 感谢您的宝贵时间,我用更合理的示例数据和 sql fiddle 更新了我的问题。

标签: postgresql aggregate


【解决方案1】:

自然地,在创建所有小提琴和描述 5 分钟后,我已经成功解决了这个问题。解决方案是一种不同的方法,而不是以奇特的方式聚合,我必须做的是检查尽可能低的拒绝标志。

with recursive
feature_tree(parent, child, name, level) as (
    select id, id, feature, 1
    from feature
    union all
    select parent, id, feature.feature, feature_tree.level + 1
    from feature_tree
    join feature on (
        feature_tree.child = feature.parent_id
    )
)
, all_features(name, deny) as (
    select distinct on (feature_tree.name)
        feature_tree.name
        , not app_user.is_superuser and deny
    from feature
    join feature_tree on (
        feature_tree.parent = feature.id
    )
    left join job_feature_relationship on (
        job_feature_relationship.feature_id = feature.id
    )
    left join job_user_relationship on (
        job_user_relationship.job_id = job_feature_relationship.job_id
    )
    join app_user on (
        app_user.id = 4
    )
    where (
        (
            app_user.is_superuser
            or job_user_relationship.user_id = app_user.id
        )
        and not exists(
          select 1 
          from feature descendant 
          where descendant.parent_id = feature_tree.child
        )
    )
    order by feature_tree.name, feature_tree.level
)
select name
from all_features
where not deny;

关键数字是distinct on (name)order by name, level。 但是我不确定性能,我不得不创建另一个 cte,因为 distinct on 不能让我正确过滤它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-08
    • 2011-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多