django - 提高 __in 查询集在 M2M 过滤中的性能

django - improve performance of __in queryset in M2M filtering

我有一个模型与另一个模型存在 M2M 关系。

这些是我的模型:

class Catalogue(models.Model):
    city = models.CharField(db_index=True,max_length=100, null=True)
    district = models.CharField(db_index=True,max_length=100, null=True)
    type = models.ManyToManyField(Type, db_index=True)
    datetime = models.CharField(db_index=True, max_length=100, null=True)


class Type(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

这是views.py:

class all_ads(generic.ListView):
    paginate_by = 12
    template_name = 'new_list_view_grid-card.html'
    def get_queryset(self):
        city_district = self.request.GET.getlist('city_district')
        usage = self.request.GET.get('usage')
        status = self.request.GET.get('status')

        last2week = datetime.datetime.now() - datetime.timedelta(days=14)

        status = status.split(',')

        if usage:
            usage = usage.split(',')
        else:
            usage = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31']

        intersections = list(set(status).intersection(usage))
        type_q = (Q(type__in=intersections) & Q(type__isnull=False))

        result = models.Catalogue.objects.filter(
            Q(datetime__gte=last2week) &
            type_q &
            ((reduce(operator.or_, (Q(city__contains=x) for x in city_district)) & Q(city__isnull=False)) |
             (reduce(operator.or_, (Q(district__contains=x) for x in city_district)) & Q(district__isnull=False)))
        ).distinct().order_by('-datetime').prefetch_related('type')

        return result

我想用一些查询过滤 MySQL 数据库,return 结果是列表视图。

它在小型数据库上运行良好,但在大型数据库上需要 10 多秒才能获得 return 结果。如果我删除 type_q 查询,需要 2 秒(减少 10 秒!)。

如何提高 __in 查询集的性能?

看起来 type_q 本身并不是真正的罪魁祸首,而是起到了乘数的作用,因为现在我们做了一个 LEFT OUTER JOIN,因此 __contains 遍历了所有组合。因此,这更像是两个一起工作的过滤器的特性

我们可以省略这个:

<strong>cat_ids</strong> = list(Catalogue.objects.filter(
    Q(*[Q(city__contains=x) for x in city_district], _connector=Q.OR) |
    Q(*[Q(district__contains=x) for x in city_district], _connector=Q.OR)
).values_list('pk', flat=True))

result = models.Catalogue.objects.filter(
    Q(datetime__gte=last2week),
    type_q,
    <strong>pk__in=cat_ids</strong>
).distinct().order_by('-datetime').prefetch_related('type')

一些数据库(MySQL 已知 不能 很好地优化子查询),甚至可以用子查询来做到这一点。所以这里我们不具体化列表,而是让 Django 使用子查询:

<strong>cat_ids</strong> = Catalogue.objects.filter(
    Q(*[Q(city__contains=x) for x in city_district], _connector=Q.OR) |
    Q(*[Q(district__contains=x) for x in city_district], _connector=Q.OR)
).values_list('pk', flat=True)

result = models.Catalogue.objects.filter(
    Q(datetime__gte=last2week),
    type_q,
    <strong>pk__in=cat_ids</strong>
).distinct().order_by('-datetime').prefetch_related('type')