AppEngine上ACL的高效组成员资格测试

时间:2012-01-24 21:56:43

标签: google-app-engine indexing filesystems acl

我正在为我的数据存储区中的对象创建访问控制列表。每个ACL条目都可以包含允许访问相应条目的所有用户ID的列表。然后我的查询获取用户可以访问的实体列表将非常简单:

select * from ACL where accessors = {userId} and searchTerms >= {search}

问题在于,它只能在达到索引条目限制之前支持2500个用户,当然,为很多用户放置ACL条目会非常昂贵,因为需要更改许多索引条目。 / p>

所以我考虑添加一组允许访问实体的用户组。这可能会大大降低每个ACL条目所需的索引条目数量,但查询会变得更长,因为我必须查询用户所在的每个可能的组:

select * from ACL where accessors = {userId} and searchTerms >= {search}
for (GroupId id : theSetOfGroupsTheUserBelongsTo) {
    select * from ACL where accessingGroups = {id} and searchTerms >= {search}
}

mergeAllTheseResultsTogether()

这需要很长时间,更难以翻页等等。

有人可以推荐一种从ACL中获取实体列表的方法,该方法不会限制访问用户的数量吗?

修改以获取更多详细信息:

我正在搜索和整理学校使用的一系列学术主题。一些主题由管理员创建,应该在整个学校范围内。其他人是由教师创建的,可能只与那些教师有关。我想创建一个类似google-docs-list的集合层次结构,将每个主题视为文档。 searchTerms字段将是主题名称中的单词列表 - 没有很多要搜索的内部文本。每个主题将至少在一个集合(组织的“根”集合)中,并且可以在多达10-20个其他集合中,所有集合都由不同的人管理。理想情况下,文档可能出现的集合数量没有上限。我在这里的努力是生成一个特定用户至少具有读取权限的所有实体的列表 - 谷歌文档中的模拟将是“所有项目”视图。

1 个答案:

答案 0 :(得分:2)

假设您的文档和组权限比用户查询更少(或时间紧迫)更改,我建议这(我正在解决类似的问题):

在您的ACL中,包含字段

  • 访问者< - 可以访问文档的所有用户ID
  • numberOfAccessors< - 每当您更改该字段时存储访问者的长度
  • searchTerms

ACL的key_name类似于"indexed_document_id||index_num"

密钥中的

index_num允许您有多个实体存储用户列表,包含超过5000个(列表中项目的数据存储限制)或者您希望列表中有多少个实体降低装载成本(虽然你不需要经常这样做)。

不要忘记要访问的文档应该是索引实体的父级。这样你就可以进行select __key__查询而不是select *(这可以避免重新序列化访问者和searchTerms字段)。您可以搜索并返回实体的parent(),而无需访问任何字段。更多关于这个和其他gae搜索设计的blog post。可悲的是,阻止帖子不包括像我们这样的ACL索引。

免责声明:我现在遇到了这种设计的问题,即用户可以访问的文档是否由关注该用户控制。这意味着如果他们关注或取消关注,可能会有大量现有文档需要添加/删除用户。如果您遇到这种情况,那么如果您遵循我的技术,您可能会陷入与我相同的洞。我目前计划通过在后台更新旧文档的索引来处理这个问题。其他人回答这个问题可能会解决它的问题 - 如果不是,我可以将其作为一个单独的问题发布。

对此数据结构的操作分析:

添加索引文档:

  1. 对于每个有权访问该文档的组,创建一个实体,其中包括可以在访问者字段中访问该文档的所有用户
  2. 如果某个字段中有太多不适合,请创建更多实体并增加该index_num值(使用分片计数器)。
  3. O(n * m)其中n是用户数,m是搜索查询数

    查询索引文档:

    1. select __key__ from ACL where accessors = {userid} and searchTerms >= {search}(虽然我不确定你为什么要“> =”实际上,在我的查询中它总是“=”)
    2. 从这些键中获取所有父键
    3. 过滤掉重复的内容
    4. 获取这些父文件
    5. O(n + m)其中n是用户数,m是搜索项的数量 - 这非常快。它使用两个索引的zig-zag合并连接(一个在访问器上,一个在searchterms上)。这假设gae索引扫描是线性的。它们可能是“=”查询的对数,但我不知道它们的索引的设计,也没有我做任何测试来验证。另请注意,您不需要加载索引实体的任何属性。

      为用户添加对特定文档的访问权限

      1. 检查用户是否已有权访问:select __key__ from ACL where accessor = {userid} and parent = {key(document)}
      2. 如果没有,请添加:select * from ACL where parent = {key(document)} and numberOfAccessors < {5000 (or whatever your max is)} limit 1
      3. 将{userid}附加到访问者并放置实体
      4. O(n)其中n是有权访问该文档的人数。

        删除用户对特定文档的访问权限

        1. select * from ACL where accessor = {userid} and parent = {key(document)}
        2. 从访问者中删除{userid}并放置实体
        3. O(n)其中n是有权访问该文档的人数。

          压缩索引

          如果您进行大量删除,则必须偶尔执行此操作。不确定检测此问题的最佳方法。

          1. 要确定是否要为特定文档压缩任何内容:select * from ACL where parent = {key(document)} and numberOfAccessors < {2500 (or half wahtever your max is)}
          2. 对于每个/任何一对:删除一个,将访问者附加到另一个
          3. O(n)其中n是有权访问该文档的人数