【问题标题】:AppEngine Datastore: Hierarchical queriesAppEngine 数据存储区:分层查询
【发布时间】:2016-09-20 22:16:18
【问题描述】:

如果您正在处理记录的层次结构,其中大多数键都有祖先,您是否必须在检索叶子之前创建所有键的链?

示例(在 Go 中):

rootKey = datastore.NewKey(ctx, "EntityType", "", id1, nil) secondGenKey = datastore.NewKey(ctx, "EntityType", "", id2, rootKey) thirdGenKey = datastore.NewKey(ctx, "EntityType", "", id3, rootKey)

您如何获得thirdGenKey 描述的记录,而不必为它上面的层次结构的所有级别声明键?

【问题讨论】:

    标签: google-app-engine go google-cloud-datastore


    【解决方案1】:

    为了获得一个单独的实体,它的键必须是全局唯一的——这是通过每个实体键在其实体组中是唯一的来强制执行的。因此,祖先路径构成实体键的固有部分。

    因此,获得具有强一致性的单个实体的唯一方法是指定其祖先路径。这可以通过 get-by-key 或祖先查询来完成。

    如果您不知道完整的祖先路径,您唯一的选择是查询实体的属性,但请记住:

    • 这在您的应用程序中可能不是唯一的
    • 您将受到最终一致性的约束。

    【讨论】:

    • 这就是我要确认的。因此,如果您的层次结构由许多祖先级别组成,您将需要构造一个具有 ID 而没有祖先的键,另一个具有其 ID 的键和该祖先的第一个键,另一个具有其 ID 的键和第二个键用于这个祖先等等,一直到你处于正确的级别来访问你的主要记录,对吗?换句话说,如果你有一个级别为 N 的记录,那么你必须构造一个包含 N 个键的链才能以强一致性检索它,对吗?
    • @DustinOprea,这是正确的。尽管您对强一致性的评论具有误导性,但所有查找(通过键显式获取)都是强一致的,它与必须存在的键祖先无关。当您执行查询(返回 0-n 个实体)而不将查询绑定到单个祖先(实体组)时,最终一致性就会发挥作用。
    • @DanMcGrath 是的.. 祖先的存在使它成为 SC,但你需要完整的链条才能下降得足够远。谢谢,丹。
    • @DustinOprea 用于查找/获取它不是存在使其强一致的祖先 - 它们都是 SC,无论是否是祖先。我只想说清楚。这是一个重要的区别。
    • 我指的是 API 这边的检索。如果没有使用祖先,则检索可能无法获得最新版本。请确认。
    【解决方案2】:

    补充 tx802 的回答:

    如果你想通过键加载一个实体,你需要它的键。如果密钥是具有父密钥的密钥,则为了形成/创建密钥,您还需要先创建父密钥。父键键的一部分,就像数字 ID 或字符串名称一样。

    从实现的角度看:datastore.Key 是一个结构体:

    type Key struct {
        kind      string
        stringID  string
        intID     int64
        parent    *Key
        appID     string
        namespace string
    }
    

    为了构造一个有父级的Key,你也必须递归地构造父级键。如果您发现创建密钥层次结构过于冗长,您可以为其创建一个辅助函数。

    为简单起见,我们假设所有键都使用相同的实体名称,并且我们只使用数字 ID。它可能看起来像这样:

    func createKey(ctx context.Context, entity string, ids ...int) (k *datastore.Key) {
        for _, id := range ids {
            k = datastore.NewKey(ctx, entity, "", id, k)
        }
        return
    }
    

    有了这个辅助函数,你的例子就变成了这样:

    k2 := createKey(ctx, "EntityType", id1, id2)
    k3 := createKey(ctx, "EntityType", id1, id3)
    

    【讨论】:

    • 感谢您的进一步澄清。看起来这个模型并不能真正适应相同实体类型的同质/递归树。例如,在我的情况下,我有一个大的分类器树(其中一行中所有子项的祖先都是父项),但是没有实用的方法来 1)存储一个表示父项的标识符作为记录上的键,也不是 2) 从另一个记录中引用这些分类器之一(因为 ID 仅对祖先是唯一的,因此递归/嵌套的祖先/父键也必须作为属性存储)。 (续...)
    • ...当然是为了立即保持一致。自然地,我可以创建一个全局唯一键并将其用于查找/引用,但它最终还是一致的。当您手头有几个离散的祖先可以用来构建复合键(动态数量的级别不可行)和/或您只有少数异构的嵌套祖先时,似乎此模型最方便实体类型,例如公司->用户->发票。想法?我很欣赏你的见解。我正在尝试找出这里的模式。
    • @DustinOprea 有多种“实用”的方式来存储关键信息。一种选择是拥有Key 类型的属性,并且该属性的值可以是任何Key 值,包括具有父项的键。另一种选择是将 ID 列表存储在属性中(假设您只使用 ID 而不是字符串名称);您甚至可以选择将它们存储在单个字符串中,例如 "id1,id2",或者作为多值属性(切片),例如 [id1, id2];并且只有这些 ID,您可以在需要时重建 Key,例如使用答案中发布的createKey() 函数。
    • 我之前尝试过“Key”类型的属性,但遇到了问题。我认为这只是文档中的不一致(比如网站上说 Datastore 可以支持整个整数大小范围,但代码只支持 int64)。我得再试一次。
    • @DustinOprea 参见Datastore reference“有效值类型为:...*Key, ...”。我假设您忘记了 * 使其成为指针类型。
    猜你喜欢
    • 1970-01-01
    • 2015-04-16
    • 1970-01-01
    • 2012-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-11
    相关资源
    最近更新 更多