Django 加权查询(注释值)
Django weighted query (annotated values)
我正在尝试创建一个查询并根据权重的自定义计算对其进行排序。
我需要一些帮助,因为我找到的解决方案确实有效,但性能不是我想要的
我有一个媒体对象。它有相关的评论、喜欢和订单。
以下查询目前有效但完全是一团糟:
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')))
但是注释中的类似操作是不可能的(尝试了一些使用和不使用 Sum() 的变体 - Django 总是抱怨注释值的类型不同。
顺便说一下,我们在这个项目中使用的是 django 1.8。
有没有好的单一查询方法来获得我想要的排序权重?
首先,您需要确保除法会产生浮点数(没有舍入)。你需要这样的东西 ():
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_related
和 select_realted
,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')))
但是注释中的类似操作是不可能的(尝试了一些使用和不使用 Sum() 的变体 - Django 总是抱怨注释值的类型不同。
顺便说一下,我们在这个项目中使用的是 django 1.8。
有没有好的单一查询方法来获得我想要的排序权重?
首先,您需要确保除法会产生浮点数(没有舍入)。你需要这样的东西 (
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_related
和 select_realted
,django 会自己处理这些事情(然而,这只是我的推测 - 如果您稍后在代码中实际使用这些外键,那么它是合理的)。