2012-02-15 20 views
1

以下代碼作爲cronjob定期運行,結果非常計算量大!主要的問題是在for循環中,我認爲這可以通過更好的過濾來提高效率,但是我不知道該怎麼做。如何更好地在appengine中使用過濾器,以便通過循環一長串實體來節省我的過濾?

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循環,我在生產中使用並更好地利用緩存的各種實體的代碼,仍然可以通過具有周期一堆用戶終於找到它需要做的操作。理想情況下,我會過濾掉所有不符合條件的用戶,然後開始循環瀏覽用戶列表 - 我可以在此處使用某種魔術彈藥來實現此目的嗎?

+1

在一個不相關的說明中;儘管你的「limit = 99999」fetch()一次只會返回1000個實體,但是如果你在不調用fetch()的情況下使用「for all_free_users:」中的用戶,你可以遍歷所有的實體。 http://stackoverflow.com/questions/264154/google-appengine-how-to-fetch-more-than-1000 – arkanciscan 2012-02-16 19:17:30

+0

感謝一個很酷的提示arkanciscan,謝謝 - 我會給它一個bash。 – user714852 2012-02-17 23:39:32

回答

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的數據存儲非常多而不是關係數據庫。

您是否可以看到將所有這些字段存儲在單個模型中的缺點?

+0

感謝您的回覆。我發佈的模型已經大大簡化以提高可讀性。實際上,每個實體都有相當多的字段 - 因此有利的是(在我沒有受過教育的情況下)將它們作爲單獨的實體。 – user714852 2012-02-17 23:38:57

+0

不要讓絕對數量的領域成爲一個令人信服的理由將他們分成不同的實體。如果它們都與相同的基本數據單元有關,那麼你只是爲自己複雜化了一些東西。 – 2012-02-18 13:49:15

2

您可以通過將數據放在一起放在一個模型中來改善這一點。例如,一個單一的實體類型的UserMembership可以擁有你所需要的字段,你可以做一個查詢:

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

這將需要額外的索引來定義,但會多跑,多快。

+0

爲了使分析起來更容易一些,我簡化了模型。我確實需要將這些模型分開,因爲它有助於使其他函數更高效一些(並且更容易理解)。這僅僅是一個非常低效的事件。 – user714852 2012-02-17 23:31:21

+0

無法交叉引用或以其他方式加入不同種類的查詢。如果你想在AppEngine中進行交叉引用,你必須*將相關數據存儲在一個實體中。如果您還希望數據因爲其他原因分別處於不同的模型中,則必須複製數據 - 將其存儲在標準化的分組表單中,並以其單獨的形式存儲一次 - 並使用一套另一方面。 – 2012-02-19 00:47:15

1

如果你想避免在其他兩個答案建議非規範化你的數據,你也可以考慮使用谷歌的新的SQL服務,而不是正常的數據存儲的:http://googleappengine.blogspot.com/2011/10/google-cloud-sql-your-database-in-cloud.html

使用SQL,你可以做到這一切在單個查詢,即使是單獨的實體。

+0

我期待在出現預覽時使用SQL服務 - 我只是有點不情願爲我的應用修補太多的「測試版」/未發佈的產品。 – user714852 2012-02-20 07:51:18