Django ORM - Условие или фильтр на LEFT JOIN

Я постараюсь быть максимально точным в этом вопросе. Представьте себе эти две модели. связь между которыми была установлена много лет назад:

class Event(models.Model):
    instance_created_date = models.DateTimeField(auto_now_add=True)
    car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name="car_events")
    ...
    a lot of normal text fields here, but they dont matter for this problem.

и

class Car(models.Model):
    a lot of text fields here, but they dont matter for this problem.
    hide_from_company_search = models.BooleanField(default=False)
    images = models.ManyToManyField(Image, through=CarImage)

Допустим, я хочу запросить количество событий для данного автомобиля:

def get_car_events_qs() -> QuerySet:
    six_days_ago = (timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=6))
    cars = Car.objects.prefetch_related(
        'car_events',
    ).filter(
        some_conditions_on_fields=False,
    ).annotate(
        num_car_events=Count(
            'car_events',
            filter=Q(car_events__instance_created_date__gt=six_days_ago), distinct=True)
    )

    return cars

По-настоящему сложной частью для этого является производительность запроса: Cars имеет 450.000 записей, а Events имеет 156.850.048. Все поля, которые я использую для запроса, проиндексированы. Выполнение запроса занимает около 4 минут, в зависимости от загрузки базы данных. До добавления признаков прошло 18 минут

В результате вышеприведенного ORM-запроса получится следующий sql:

SELECT
  "core_car"."id",
        COUNT("analytics_carevent"."id") FILTER (WHERE ("analytics_carevent"."event" = 'view'
            AND "analytics_carevent"."instance_created_date" >= '2022-05-10T07:45:16.672279+00:00'::timestamptz
            AND "analytics_carevent"."instance_created_date" < '2022-05-11T07:45:16.672284+00:00'::timestamptz)) AS "num_cars_view",
    LEFT OUTER JOIN "analytics_carevent" ON ("core_car"."id" = "analytics_carevent"."car_id")
WHERE 
 ... some conditions that dont matter
GROUP BY
    "core_car"."id"

Я почему-то подозреваю, что это FILTER является проблемой. Я пробовал с

.annotate(num_car_events=Count('car_events'))

и перемещение car_events__instance_created_date__gt=six_days_ago в filter:

.filter(some_conditions_on_fields=False, car_events__instance_created_date__gt=six_days_ago)

Но, конечно, это отфильтрует автомобили без событий, что не то, что нам нужно - но это супер быстро! Я немного повозился с этим в raw sql и пришел к хорошему рабочему примеру, который я теперь хотел бы написать в ORM, поскольку мы не хотим использовать rawsql. Этот запрос занимает 2.2s, что находится в нашей допустимой границе, но гораздо меньше, чем 18 минут.

SELECT
    "core_car"."id",
    COUNT(DISTINCT "analytics_carevent"."id") AS "num_cars_view",
FROM
    "core_car"
    LEFT JOIN "analytics_carevent" ON ("core_car"."id" = "analytics_carevent"."car_id" AND "analytics_carevent"."event" = 'view' AND "analytics_carevent"."instance_created_date" > '2022-05-14T00:00:00+02:00'::timestamptz
        AND "analytics_carevent"."instance_created_date" <= '2022-05-15T00:00:00+02:00'::timestamptz)
    
WHERE (some conditions that dont matter)
GROUP BY "core_car"."id";

Мой вопрос теперь в следующем: Как я могу сделать вышеприведенный запрос в ORM? Мне нужно поместить "фильтр" или условия в left join. Если я использую filter(), то он просто поместит их в предложение where, что неправильно. Я пробовал:

 two_days_ago = (timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=2))
    cars = Car.objects.prefetch_related(
        'car_events',
    ).filter(some_filters,)
    cars = cars.annotate(events=FilteredRelation('car_events')).filter(car_events__car_id__in=cars.values_list("id", flat=True), car_events__instance_created_date__gt=six_days_ago)

Но я не думаю, что это правильно. Мне также нужен счетчик аннотаций.

Используется Django 4 и последний релиз python на момент написания статьи. :)

Большое спасибо!

TLDR: Наложение фильтра или условия на LEFT JOIN в django, вместо queryset.filter()

Вернуться на верх