2016-11-23 57 views
1

我試圖創建一個查詢,並根據權重的自定義計算對其進行排序。Django加權查詢(註釋值)

我需要一些幫助,因爲該解決方案我來確實工作,但性能並不在那裏,我想它是

我已經是一個媒體對象。它有相關的評論,喜歡和訂單。

什麼當前工作,但一個完整的哈克爛攤子以下查詢:

products = Media.objects \ 
     .select_related(
      'image', 
      'currency', 
      'user', 
      'user__image', 
     ) \ 
     .prefetch_related('category', 'tags') \ 
     .exclude(is_deleted=1) \ 
     .filter(Q(category__category__in=categories) | Q(tags__tag__title=query)) \ 
     .annotate(order_count = Count('orders', distinct=True)) \ 
     .annotate(comment_count = Count('comments', distinct=True)) \ 
     .annotate(like_count = Count('likes', distinct=True)) \ 
     .annotate(weight = Count(0)) \ 
     .distinct() 

    for m in products.iterator(): 
     initial_weight = int(m.order_count)*40 + int(m.comment_count)*4 + int(m.like_count)*4 + int(m.clicks) 
     m.weight  = float(float(initial_weight) - float(m.views/50)) 

正如你看到的,我單獨註釋我將使用的所有參數,然後做一個愚蠢的迭代全算術針對查詢集中每個項目的操作都非常次優。

有一件事我試圖做的是以下幾點:在註釋

products = Media.objects \ 
     .select_related(
      'image', 
      'currency', 
      'user', 
      'user__image', 
     ) \ 
     .prefetch_related('category', 'tags') \ 
     .exclude(is_deleted=1) \ 
     .filter(Q(category__category__in=categories) | Q(tags__tag__title=query)) \ 
     .annotate(weight = Count('orders', distinct=True) * 40 + Count('comments', distinct=True) * 4 + Count('likes', distinct=True) - F('views')/50 + F('clicks')) 

但類似操作,不可能(試圖與不求和()有一些變化 - Django的總是抱怨註解值是不同的型。

通過我們使用Django 1.8本項目的方式。

是否有一個良好的單一查詢的形式給出,這是讓我所需的排序權重?

編輯:

我的最終解決方案(感謝Назар)

media_list = Media.objects \ 
     .select_related('user', 'user__image', 'image', 'currency') \ 
     .filter(user__in=suggested_users) \ 
     .filter(created_at__gt=seven_days_ago) \ 
     .exclude(sold=True) \ 
     .annotate(order_count = Count('orders', distinct=True)) \ 
     .annotate(comment_count = Count('comments', distinct=True)) \ 
     .annotate(like_count = Count('likes', distinct=True)) \ 
     .annotate(
      initial_weight=ExpressionWrapper((F('order_count') * 40 + F('comment_count') * 4 + \ 
          F('like_count') * 4 + F('clicks')), output_field=FloatField()) 
     ) \ 
     .annotate(
      views_divided=ExpressionWrapper(F('views')/Decimal(50.0), output_field=FloatField()) 
     ) \ 
     .annotate(weight=F('initial_weight') - F('views_divided')) \ 
     .distinct() 

回答

1

首先,你需要確保部門將產生浮動(不四捨五入)。您需要像這樣(disgracefully stolen here):

ExpressionWrapper(
    (F('views')/Decimal(50.0), 
    output_field=FloatField()), 
) 

因此,查詢應該是這樣的:

products = Media.objects \ 
    .exclude(is_deleted=1) \ 
    .filter(Q(category__category__in=categories) | Q(tags__tag__title=query)) \ 
    .annotate(order_count = Count('orders', distinct=True)) \ 
    .annotate(comment_count = Count('comments', distinct=True)) \ 
    .annotate(like_count = Count('likes', distinct=True)) \ 
    .annotate(weight = Count(0)) \ 
    .annotate(
     initial_weight=ExpressionWrapper(
      F('order_count') * 40 + F('comment_count') * 4 + \ 
      F('like_count') * 4 + F('clicks'), 
      output_field=FloatField() 
     ) 
    ) \ 
    .annotate(
     views_divided=ExpressionWrapper((F('views')/Decimal(50.0), 
             output_field=FloatField())) 
    ) \ 
    .annotate(weight=F('initial_weight') - F('views_divided')) \ 
    .distinct() 

相貌醜陋,但應該工作(我認爲)。

在邊注 - 如果你只需要計算weight,你實際上並不需要使用prefetch_relatedselect_realted,Django會照顧這些東西本身(不過,這只是我的猜測 - 如果你真的使用這些外鍵在代碼後面,那麼它是合理的)。

+0

非常感謝。 是的,它有點醜,但ExpressionWrapper做的工作! 只有一個音符(有點奇怪)neede dto包裹initial_weight和ExpressionWrapper!我將在最終解決方案中進行編輯。 PS我需要select_related用於其他目的,但它不需要的事實也很有價值!太感謝了。 –

+0

很高興幫助。如果有人遇到你的問題,我也會更新答案。 –