提取相关ID并汇总到新列中

时间:2019-03-25 17:57:52

标签: python django postgresql orm aggregate-functions

所以我的问题来自对查询的复杂性的不了解。这个问题有一些背景故事。

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

设置如下:我有一个汽车模型,它具有ID,类别,汽车类型,引擎等。其次,有一个地址表,我可以用它来处理各种事情。

我现在想做的是:

我想创建Google Ads专用的.csv文件。为了向用户显示“相关内容”,此文件需要带有聚合整数的特定列。含义:您已经看到了Car 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秒。我不确定我是否有任何遗漏。它有点用,但对于较大的样本量将不起作用。我在一个大约有14000辆汽车的DB上运行了它,但是它从未完成。

所以总结一下:我想创建一个函数,在数据库中创建一个具有相似汽车ID的列。

任何人都有在何处提高效率的指针?并且请问是否还有其他问题,我忘了提些什么!

1 个答案:

答案 0 :(得分:0)

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