2013-07-10 34 views
0

你好,我做了一個函數,它會根據創建它們的時間來返回帖子,並根據它們的投票數量進行排名。這是一個飼料。我不確定如果這是一種有效的方法,那麼我很重要,因爲這可能會篩選數千個帖子。我在這裏簡化了一下,並簡要解釋了每一步。我目前的擔憂和整個職能的一個職位(沒有註釋)在之後。如何優化此功能以返回Feed中的帖子。 Django

開始小時數參數是多少小時前獲得帖子。例如,如果startHours = 6,則只會返回六小時前創建的帖子。

def rank(request, startHours): 

首先,我通過票

unorderedPosts=Post.objects.order_by('-votes') 

然後帖子被排除由類別用戶已經指定

if request.user.is_authenticated(): 
     preferences=request.user.categorypreference_set.filter(on=False) 
     for preference in preferences: 
       unorderedPosts=unorderedPosts.exclude(category_name=preference.category_name) 

然後我使endHours,這始終是4小時訂購的所有文章提前開始時間參數

endHours=startHours+4   #4 hour time window 

現在我分割無序的郵件,並從現在開始只在時間窗口startHours中創建結束小時。例如,如果startHours = 4,則只會返回4小時前但8小時前創建的帖子。

posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours))) 

    posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours))) 

現在我做一個循環,直到將至少一種後發現,有適合的時間窗口創建日期的時間窗口向後移動。我使檢查變量防止無限循環(如果在200小時內沒有發現任何帖子,循環將退出)。

count=posts.count() 
    check=endHours 
    while count<1 and endHours<(check+200): 
     endHours+=4 
     startHours+=4 
     posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours))) 
     posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours))) 
     count=posts.count() 
     if count>=1: return posts, endHours 

    return posts 

我最大的擔心是在開始時製作所有帖子的查詢集。此功能是爲了在小時間窗口中返回帖子,是否會通過對數據庫中的所有帖子進行排名而不必要地降低速度?我知道django/python查詢集相當高效,但不會對包含數千個對象的集合進行排序對於此函數來說很麻煩嗎?

如果這是一個問題,我怎樣才能讓它更有效率,同時保持一切可用?

這裏是整個事情的職位。

def rank(request, startHours): 

    unorderedPosts=Post.objects.order_by('-upVote') 

    if request.user.is_authenticated(): 
     preferences=request.user.categorypreference_set.filter(on=False) 
     for preference in preferences: #filter by category preference 
       unorderedPosts=unorderedPosts.exclude(category_name=preference.category_name) 


    endHours=startHours+4  #4 hour time window 

    posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours))) 

    posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours))) 

    count=posts.count() 
    check=endHours 

    while count<1 and endHours<(check+200): 
     endHours+=4 
     startHours+=4 
     posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours))) 
     posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours))) 
     count=posts.count() 
     if count>=1: return posts 

    return posts 

回答

1

您主要關心的不是您需要擔心的事情。查看When querysets are evaluated上的文檔 - 您可以無限期地爲查詢集定義和添加子句,直到您調用實際需要觸及數據庫的內容時,纔會真正對數據庫運行子句。

什麼將需要多個查詢遍歷時間,直到你點擊一個有帖子的窗口。如果您在一次通話中查看最新的created時間,則可以獲得更好的效果,使用該時間計算出您的窗口,然後根據該時間限制帖子,然後按投票數排序。

喜歡的東西:

unorderedPosts = Post.objects.all() 
if request.user.is_authenticated(): 
    preferences=request.user.categorypreference_set.filter(on=False) 
    for preference in preferences: #filter by category preference 
     unorderedPosts=unorderedPosts.exclude(category_name=preference.category_name) 
latest_post_datetime = unorderedPosts.aggregate(Max('created'))['created__max'] 

original_start_time = datetime.datetime.now() - datetime.timedelta(hours=startHours)  
latest_post_day_start_time = datetime.datetime.combine(latest_post_datetime.date(), original_start_time.time()) 
# a timedelta guaranteed to be less than 24 hours 
time_shift = latest_post_day_start_time - latest_post_datetime 
timewindow = datetime.timedelta(hours=4) 
if time_shift.days >= 0: 
    extra_windows_needed = time_shift.seconds/timewindow.seconds 
else: 
    # negative timedeltas store negative days, then positive seconds; negate 
    extra_windows_needed = -(abs(time_shift).seconds)/timewindow.seconds 
start_time = latest_post_day_start_time - (timewindow * (extra_windows_needed + 1)) 
posts = unorderedPosts.filter(created__gte=start_time).order_by('-upVote') 
return posts 

這裏的數學是唯一正確的,只要你在你的窗口小時數(4)平均地分成一天 - 否則計算正確的偏移量變得棘手。基本上,你需要花費時間偏移mod時間窗口長度,並且我正在利用這個事實,即如果你最終在同一個日曆日,我知道mod四個小時部分的日子。

此外,它不包含結束時間,因爲您的原始邏輯不會爲初始startHours期間強制執行一次。如果沒有任何內容,它只會將開始時間移回原處,因此您不必擔心最近出現的內容。

此版本最多可以創建三個數據庫查詢 - 一個獲取登錄用戶的類別首選項,一個獲取latest_post_datetime,一個獲取posts,並且有信心至少有一個匹配的帖子。

您也可以考慮剖析,看看你的數據庫後端做一個子查詢更好地排除不必要的類別:

if request.user.is_authenticated(): 
    unorderedPosts = unorderedPosts.exclude(category_name__in=request.user.categorypreference_set.filter(on=False).values_list('category_name') 

隨着__in lookup docs注,性能在這裏與不同的數據庫後端變化。

+0

你提出了一些很好的建議!我現在基於下一個最新帖子創建時間窗口,而不是遍歷每個連續窗口。我把排序的順序先轉換成類別,然後再轉換成類別,最後是投票。我看着嘗試一個子查詢,但我發現我的後端(mysql)不如那個方法。非常感謝您的洞察! –

+0

時間窗口的東西很大程度上取決於你想要做的事情 - 如果你正在尋找,比如說,不管時間多少,都有一定的最小數量的帖子,還有其他方法可以做到這一點。而且,是的,子查詢有點碰巧。 –