使用Q对象时如何对查询结果进行排名

时间:2019-02-14 22:45:37

标签: django postgresql django-queryset

我正在使用Django构建图像数据库。用户应该能够使用其标题,艺术家的名字,其位置等来搜索艺术品。我目前正在使用Q Objects构建查询:

query_search = request.GET.get('search')
terms = [term.strip() for term in query_search.split()]
q_objects = Q()

for term in terms:
    q_objects.add(Q(title__icontains=term), Q.OR)
    artists = Artist.objects.filter(Q(name__istartswith=term) | Q(name__icontains=' ' + term))
    q_objects.add(Q(artists__in=artists), Q.OR)
    q_objects.add(Q(location_of_creation__name__istartswith=term), Q.OR)
    keywords = Keyword.objects.filter(name__icontains=term)
    q_objects.add(Q(keywords__in=keywords), Q.OR)

querysetList = (Artwork.objects.filter(q_objects)
                               .exclude(published=False)
                               .order_by('title',
                                         'location_of_creation',
                                         'artists')
                               .distinct())

我想按相关性排序结果(例如,在艺术家名称匹配之前显示标题匹配)。

据我了解,这通常是通过使用anateate()完成的:设置排名编号,然后由order_by()使用。但是,我不确定使用Q对象时如何执行此操作。是否可以“注释” Q对象?还是我在这里树错树了?

1 个答案:

答案 0 :(得分:2)

您应该能够使用Case语句执行此操作。用reduce构造Q对象有点丑陋,但一般的想法应该适用。 Link to the docs

Artwork.objects.annotate(
    rank=Case(
        When(reduce(operator.or_, (Q(title__icontains=term) for term in terms)), then=Value(1)),
        When(reduce(operator.or_, (Q(location_of_creation__name__istartswith=term) for term in terms)), then=Value(2)),
        default=Value(99),
        output_field=IntegerField(),
    )
).order_by(
    'rank',
    'title',
    'location_of_creation',
    'artists'
)