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



下面的代码是作为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循环仍然需要在一群用户中循环,才能最终找到需要对其进行操作的少数用户。理想情况下,我会过滤掉所有不符合标准的用户,然后开始在用户列表中循环——有没有什么灵丹妙药可以让我在这里实现这一点?

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

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的数据存储在很大程度上是而不是关系数据库。

你能看到将所有这些字段存储在一个模型中有什么缺点吗?

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

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

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

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

使用SQL,您可以在单个查询中完成所有这些操作,甚至可以使用单独的实体。

最新更新