【问题标题】:Fetch related ID's and aggregate in a new column获取相关 ID 并在新列中聚合
【发布时间】:2019-03-25 17:57:51
【问题描述】:

所以我的问题来自对查询复杂性的理解不足。这个问题的背景故事。

这是一个汽车租赁和搜索网站,最初是我的个人项目。我为此使用 Django 2.1 以及 Postgres。

设置如下:我有一个车型,它有一个ID、一个类别、一个车型、一个引擎等。其次,有一个地址表,我用它来做各种各样的事情。

我现在想做的是:

我想创建特定于 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() 调用工作,因为我想提取特定字段

Select Related Doc Reference

2

只给我我想要的字段,因为我不想发送整个模型,这也有效。

Only Doc Reference

所以选择和获取正确的数据不是问题,而是:

真正的问题:

以下代码应在表中创建一列。此列应具有相似区域(城市和市区)中汽车的聚合 ID。遗憾的是,这是我使用的 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 秒。我不确定我是否遗漏了什么。它有点工作,但不适用于更大的样本量。我在一个大约有 14k 辆汽车的数据库上运行它,但它从未完成。

总结一下:我想创建一个函数,在数据库中创建一个包含相似汽车的聚合 ID 的列。

任何人都知道在哪里可以提高效率?如果还有其他问题,请询问,我忘了提一些事情!

【问题讨论】:

    标签: python django postgresql orm aggregate-functions


    【解决方案1】:

    除非您对结果进行分页,否则在 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},
            ])
    

    【讨论】:

    • 是的,分页是以后的目标.. :(
    • 为您添加了另一个选项。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2020-01-09
    • 2015-04-10
    • 1970-01-01
    相关资源
    最近更新 更多