2013-07-10 2 views
0

Привет, я создал функцию, которая будет возвращать сообщения, основанные на том, когда они были созданы и ранжированы по количеству голосов, которые у них есть. Это для фида. Я не уверен, что если это эффективный способ сделать это, и очень важно, чтобы я понял это правильно, потому что это потенциально могло бы просеиваться через тысячи сообщений. Я упростил его здесь и кратко объяснил каждый шаг. Мои текущие проблемы и пост целой функции (без аннотаций) после.Как оптимизировать эту функцию для возврата сообщений в фид. 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 для endHours. Например, если 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) делит равномерно в день - в противном случае расчет правильного смещения становится сложнее. В принципе, вам нужно взять временную шкалу времени смещения времени, и я использую тот факт, что если вы закончите в тот же день календаря, я знаю, что в течение четырех дней работает часть часов.

Кроме того, оно не включает в себя конечное время, потому что исходная логика не применяет один для начального периода startHours. Это только сдвинет время начала, если в нем нет ни одного, поэтому вам не нужно беспокоиться о вещах, которые слишком недавно появляются.

Эта версия составляет не более трех запросов БД - одна для получения предпочтений категории для зарегистрированного пользователя, одна для получения latest_post_datetime и одна для получения posts, с уверенностью иметь хотя бы одно подходящее сообщение.

Вы могли бы также рассмотреть профилирование, чтобы увидеть, если ваш DB задний конец делает лучше с подзапросом, чтобы исключить нежелательные категории:

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

Временные окна сильно зависят от того, что вы пытаетесь сделать - если вы ищете, скажем, определенное минимальное количество сообщений, независимо от времени, есть другие способы сделать это. И, да, подзапросы - это хит или промах. –

Смежные вопросы