如何通过循环遍历一长串实体来更好地使用appengine中的过滤器来节省过滤?

时间:2012-02-15 18:59:09

标签: google-app-engine google-cloud-datastore python-2.7 gql

下面的代码作为一个cronjob定期运行,结果计算成本非常高!主要问题在于for循环,我认为使用更好的过滤可以使效率更高一些,但是我不知道如何做到这一点。

free_membership_type = MembershipType.all().filter("membership_class =", "Free").filter("live =", True).get()
all_free_users = UserMembershipType.all().filter("membership_active =", True)
all_free_users = all_free_users.filter("membership_type =", free_membership_type).fetch(limit = 999999)
if all_free_users:
    for free_user in all_free_users:
        activation_status = ActivationStatus.all().filter("user = ", free_user.user).get()
        if activation_status and activation_status.activated:
            documents_left = WeeklyLimits.all().filter("user = ", free_user.user).get()
            if documents_left > 0:
                do something...

代码使用的模型是:

class MembershipType(db.Model):
    membership_class = db.StringProperty()
    membership_code = db.StringProperty()
    live = db.BooleanProperty(default = False)

class UserMembershipType(db.Model):
    user = db.ReferenceProperty(UserModel)
    membership_type = db.ReferenceProperty(MembershipType)
    membership_active = db.BooleanProperty(default = False)

class ActivationStatus(db.Model):
    user = db.ReferenceProperty(UserModel) 
    activated = db.BooleanProperty(default = False)

class WeeklyLimits(db.Model):
    user = db.ReferenceProperty(UserModel) 
    membership_type = db.ReferenceProperty(MembershipType) 
    documents_left = db.IntegerProperty(default = 0)

我在生产中使用的代码确实更好地利用了各种实体的缓存,但for循环仍然需要遍历一堆用户才能最终找到需要进行操作的少数几个。理想情况下,我会过滤掉所有不符合标准的用户,然后才开始循环浏览用户列表 - 我是否可以使用某种神奇子弹来实现这一目标?

3 个答案:

答案 0 :(得分:2)

您可以通过将数据更紧密地存储在单个模型中来改善这一点。例如,单个实体类UserMembership可以包含您需要的所有字段,并且您可以执行单个查询:

.filter("membership_type =", "FREE").filter("status =", "ACTIVE").filter("documentsLeft >", 0)

这需要定义一个额外的索引,但运行速度要快得多。

答案 1 :(得分:2)

您可能正在寻找的魔力是非规范化。在我看来,这些类都可以有意义地组合成一个模型:

class Membership(db.Model):
    user = db.ReferenceProperty(UserModel)
    membership_class = db.StringProperty()
    membership_code = db.StringProperty()
    live = db.BooleanProperty(default = False)
    membership_active = db.BooleanProperty(default = False)
    activated = db.BooleanProperty(default = False)
    documents_left = db.IntegerProperty(default = 0)

然后,您可以使用一个查询来完成所有过滤。

过度规范化是AppEngine开发中常见的反模式。您发布的模型看起来也可能是关系数据库的表定义(尽管,它是否可以说比它更加划分区域化,而且AppEngine的数据存储非常关系数据库数据库中。

您是否看到将所有这些字段存储在单个模型中的任何缺点?

答案 2 :(得分:1)

如果您希望避免按照其他两个答案的建议对数据进行非规范化,您还可以考虑使用Google的新SQL服务而不是普通数据存储:http://googleappengine.blogspot.com/2011/10/google-cloud-sql-your-database-in-cloud.html

使用SQL,您可以在单个查询中完成所有这些操作,即使使用单独的实体也是如此。