祖先查询直接后代 – Google Datastore

huangapple go评论87阅读模式
英文:

Ancestor Query Direct Descendants - Google Datastore

问题

我正在使用Go和Google Datastore在Google App Engine中构建一个目录应用程序。我正在使用Datastore的Ancestor功能来管理不同的产品类别。以下是一些数据的示例:

Musical Instruments -> Guitars -> Gibson -> Les Paul

Musical Instruments -> Guitars -> Fender -> Stratocaster

Musical Instruments -> Bass Guitars -> Music Man -> Stingray

"Musical Instruments"是根实体。当我点击它时,我希望看到GuitarsBass Guitars,但实际上我看到的是所有作为Musical Instruments后代的实体,一直到最后一个实体。这不是我想要的。我只对Musical Instruments的直接后代感兴趣。

一些帖子(比如这个)建议在数据存储中创建一个字段来跟踪直接父级。但是,如果我要手动跟踪父实体,为什么还要使用Ancestor功能呢?与匹配直接父级字段的过滤查询相比,它会更快吗?

以下是获取类别的方法:

func (cat *Category) GetCategories(r *http.Request, pk string) ([]CategoryReturn, error) {
    //获取上下文
    c := appengine.NewContext(r)

    var q *datastore.Query
    var err error

    //获取父键
    k, err := datastore.DecodeKey(pk)

    if err != nil {
        //处理错误
        return []CategoryReturn{}, err
    }

    q = datastore.NewQuery("Category").Ancestor(k)

    //填充类别切片
    var categories []CategoryReturn
    keys, err := q.GetAll(c, &categories)

    if err != nil {
        //处理错误
        return []CategoryReturn{}, err
    }

    //创建返回对象
    results := make([]CategoryReturn, 0, 20)

    for i, r := range categories {
        k := keys[i]
        y := CategoryReturn {
            Name: r.Name,
            Id: k.IntID(),
            Key: k.Encode(),
        }

        results = append(results, y)
    }

    return results, nil
}
英文:

I'm building a catalog application in Google App Engine, using Go, and Google Datastore. I'm using the Ancestor features of the datatore to manage different product categories. Here's an example of some data:

Musical Instruments -> Guitars -> Gibson -> Les Paul

Musical Instruments -> Guitars -> Fender -> Stratocaster

Musical Instruments -> Bass Guitars -> Music Man -> Stingray

Musical Instruments is the root entity. When I click it, I expect to see Guitars and Bass Guitars, but instead I see everything that is a descendant of Musical Instruments all the way to the last entity. Which is not what I'm looking for. I'm only interested in the direct descendants of Musical instruments at this point.

Some posts, like this one, suggest creating a field in the datastore to track direct parents. But, if I'm going to track parent entities manually, why use the Ancestor features at all? Would it be faster than filtering queries who match a direct parent field?

Here's the method for getting categories:

func (cat *Category) GetCategories(r *http.Request, pk string) ([]CategoryReturn, error) {
//get context
c := appengine.NewContext(r)

var q *datastore.Query
var err error

//get parent key
k, err := datastore.DecodeKey(pk)

if err != nil {
    //handle error
    return []CategoryReturn{}, err
}

q = datastore.NewQuery("Category").Ancestor(k)

//populate category slices
var categories []CategoryReturn
keys, err := q.GetAll(c, &categories)

if err != nil {
    //handle error
    return []CategoryReturn{}, err
}

//create return object
results := make([]CategoryReturn, 0, 20)

for i, r := range categories {
    k := keys[i]
    y := CategoryReturn {
        Name: r.Name,
        Id: k.IntID(),
        Key: k.Encode(),
    }

    results = append(results, y)
}

return results, nil

}

答案1

得分: 0

你需要考虑一下你的应用程序中确实需要强一致性的部分,然后考虑哪些实体和实体组需要参与相应的查询和事务(现在可以在跨组事务中最多有25个实体组),但是你对祖先的使用方式让我感到警觉。

很容易被实体组所困扰(我也有过这种经历!),认为它们是一种逻辑上组织数据模型的方式,但这可能会导致问题,例如在不必要的大实体组上出现写入争用。

相反,最好考虑一下应用程序中需要强一致性的点,并围绕这些点设计实体组。

在这种情况下,我可能只会有一个parentCategory属性(类型为datastore.Key)。然后,你可以像这样查询音乐乐器的子类别:

k := datastore.NewKey(c, "Category", "Musical Instruments", 0, nil)
q := datastore.NewQuery("Category").Filter("parentCategory =", k)

(我对Go还不太熟悉,上述代码可能只是一个近似)

假设在每个类别中,你有某种Product,并且你想在树的任何级别上查询给定Category中的所有Product(例如,在吉他中的Telecaster,或在音乐乐器中的Minimoog),那么你可能需要一个多值属性(在Go中,我猜这可能是一个[]datastore.Key切片),表示Category树的分支。

英文:

You will need to think about any parts of your application that do need strong consistency and then consider what entities & entity groups will need to participate in the corresponding queries and transactions (you can now have up to 25 in a cross-group transaction), but your use of ancestors in this way rings alarm bells for me.

It's easy to get caught out (I have!) with entity groups by thinking of them as a way to logically structure your data model, but this can cause problems down the line where you end up with write contention on an unnecessarily large entity group.

Instead, it's best to think about the points in your application where you need strong consistency and design your entity groups around that.

In this scenario I would probably just have a parentCategory property (of type datastore.Key). You can then query for subcategories of Musical Instruments like this:

k := datastore.NewKey(c, "Category", "Musical Instruments", 0, nil)
q := datastore.NewQuery("Category").Filter("parentCategory =", k)

(I'm quite new to Go so the above might be an approximation)

Assuming within each category you have some kind of Product and you want to query for all Products within a given Category at any level in the tree (e.g., Telecaster in Guitars, or Minimoog in Musical Instruments then you will probably need a multi-value property (in Go I guess this would probably be a []datastore.Key slice) representing the branch of the Category tree.

huangapple
  • 本文由 发表于 2015年10月8日 03:33:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/33000904.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定