Получить связанные идентификаторы и агрегат в новом столбце - PullRequest
0 голосов
/ 25 марта 2019

Итак, моя проблема в плохом понимании сложности моего запроса. Немного предыстории этого вопроса.

Это сайт по прокату и поиску автомобилей, который начался как мой личный проект. Для этого я использую Django 2.1, а также Postgres.

Настройка выглядит следующим образом: у меня есть модель автомобиля с идентификатором, категорией, типом автомобиля, двигателем и т. Д. Во-вторых, есть таблица адресов, которую я использую для всех видов вещей.

Теперь я хотел бы сделать следующее:

Я хочу создать специфичные для Google Ads файлы .csv. Этот файл нуждается в определенном столбце с агрегированными целыми числами, чтобы показать «связанный контент» для пользователя. Значение: Вы видели Автомобиль A, здесь представлен список похожих или похожих автомобилей в этой области: Автомобиль K, O и Q.

У меня действительно нет проблем с созданием csv из моих данных, но моя проблема скорее в том, чтобы создать запрос, чтобы это работало. В качестве первого шага у меня есть следующее:

cars = Car.objects
   .select_all_related()
   .only(
      'id',
      'name',
      'address__city',
      'address__city_area',
      'images'
  )

1

select_all_related присоединяется к таблице адресов, потому что там находится машина. Это также заставляет мой вызов only () работать, так как я хочу вытащить определенные поля

Выбрать ссылку на соответствующий документ

2

дает мне только те поля, которые я хочу, так как я не хочу отправлять всю модель в любом случае, это также работает.

Только справочный документ

Таким образом, выбор и получение правильных данных не проблема, но:

Реальная проблема:

Следующий код должен создать столбец в таблице. В этом столбце должны быть агрегированные идентификаторы автомобилей, которые находятся в аналогичной области (город и район города). И это, к сожалению, требование Google Ads, которым я пользуюсь.

def find_similiar_cars_in_same_city(cars: QuerySet):
    """Annotate the QuerySet with a field called similar_cars_ids containing
       a list of ad IDs or None if there are none."""
    similar_cars_queryset = Cars.objects.filter(
            address__city=OuterRef('address__city'),
            address__city_area=OuterRef('address__city_area'),
              ).exclude(id=OuterRef('id')).values_list(ArrayAgg('id'), flat=True)
    # Hack: Remove GROUP BY that Django adds when adding ArrayAgg.
    similar_cars_queryset.query.group_by = []

    cars = cars.annotate(similar_cars_ids=Subquery(
        similar_cars_queryset,
        output_field=ArrayField(models.IntegerField())
    ))
    return cars

И это вроде работает. просто берет навсегда. Вы также можете увидеть комментарий, который я сделал в коде, что annotate () на самом деле группирует, по которому я действительно не хочу здесь. Я управляю всем на месте, и даже наличие 10 машин занимает около 12 секунд Я не уверен, что я что-то упустил. Это вроде работает, но не будет работать для большего размера выборки. Я столкнулся с БД с примерно 14 тысячами машин, и он так и не закончился.

Итак, подведем итог: я хочу создать функцию, которая создает в БД столбец с агрегированными идентификаторами похожих автомобилей.

У кого-нибудь есть указатель на то, где это сделать эффективнее? И, пожалуйста, спросите, есть ли еще вопросы, и я забыл кое-что упомянуть!

1 Ответ

0 голосов
/ 25 марта 2019

Если вы не разбиваете на страницы результаты, может быть проще обрабатывать их в python.

cars = Car.objects
       .select_all_related()
       .only(
          'id',
          'name',
          'address__city',
          'address__city_area',
          'images'
      )

cars_in_area_map = defaultdict(set)
for car in cars:
    cars_in_area_map[(car.address.city, car.address.city_area)].add(car.id)

# Generate csv:
data = [
    car.id,
    car.name,
    car.address.city,
    car.address.city_area,
    car.image,
    {id for id in cars_in_area_map[(car.address.city, car.address.city_area)] if id != car.id},
]

Если вам нужно разбить их на страницы, вы можете попробовать сделать это по адресу:

data = []

addresses = Address.objects.prefetch_related('car_set')
for address in addresses:
    cars = list(address.car_set.all())
    for car in cars:
        data.append([
            car.id,
            car.name,
            address.city,
            address.city_area,
            car.image,
            {c.id for c in cars if c.id != c},
        ])
...