【问题标题】:Sorting by number of string matches within Django queryset按 Django 查询集中的字符串匹配数排序
【发布时间】:2015-12-08 12:21:32
【问题描述】:

我有一个模型,它有一个以逗号分隔的标签列表的标签字段。给定一个标签字符串列表,我构建了一个查询集,用于查找我的模型中至少包含一个标签的实例。我有这样的工作:

tag_list = ["tag1", "tag2"]

MyModel.objects.filter(
    reduce(
        lambda x, y: x | y, [Q(tags__icontains=tag) for tag in tag_list]))

但是现在我想按每个项目具有的匹配标签的数量以降序对查询集进行排序。我一直在尝试找到正确的方法来注释包含匹配计数但运气不佳的字段。有什么指点吗?

【问题讨论】:

  • 我认为在 python 中而不是在数据库中进行排序会更有效。一旦你得到一个至少有一个标签的list 对象,你可以使用传递给排序比较的MyModel.tags.count() 属性对其进行排序 - 这就像根据一个整数字段对对象列表进行排序一样跨度>

标签: python django django-queryset


【解决方案1】:

我认为在 python 中进行排序而不是在数据库中进行排序会更有效。一旦你得到一个至少有一个标签的list 对象,你可以使用传递给排序比较的MyModel.tags.count() 属性对其进行排序 - 这就像根据一个整数字段对对象列表进行排序一样

# your query
objects = MyModel.objects.filter('...')

# sort in place
objects.sort(
    # set the sorting comparison to tag count
    key = lambda obj: obj.tags.count(),
    # if you want a descending comparison, highest tag count first
    # reverse = True
)

我说这会更高效,因为我假设您无论如何都会使用所有对象,而不是说只获取第一个(具有最高标记数)。因此,您可以在 python 本身中进行比较,而不是让数据库进行比较。

【讨论】:

  • objects 是一个查询集,因此它没有排序功能。另外,我需要我的结果是查询集,因为我在 ListView 的子类中覆盖了 get_queryset。最后,obj.tags.count() 如何给我匹配标签的数量?
【解决方案2】:

我想出了一个(复杂的)解决方案:

from django.db.models import Case, ExpressionWrapper, IntegerField, Q, Value, When

tag_list = ["tag1", "tag2"]

qs = MyModel.objects.filter(
    reduce(
        lambda x, y: x | y, [Q(tags__icontains=tag) for tag in tag_list]))

check_matches = map(
    lambda x: Case(
        When(Q(tags__icontains=x), then=Value(1)),
            default=Value(0)),
    tag_list)
count_matches = reduce(lambda x, y: x + y, check_matches)
qs = qs.annotate(
    matches=ExpressionWrapper(
        count_matches,
        output_field=IntegerField()))
qs = qs.order_by('-matches')

【讨论】:

  • 我试图复制这个解决方案,却发现我无法重新创建它。我被送回我的整个桌子。您是否有此查询的任何示例可用或能够提供更多上下文?我在这里使用您的示例 Stack Question 发布了类似的问题
猜你喜欢
  • 1970-01-01
  • 2020-08-16
  • 2016-03-07
  • 2012-09-30
  • 2021-11-26
  • 1970-01-01
  • 2012-02-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多