【问题标题】:How to do search with priority in django?如何在 django 中优先搜索?
【发布时间】:2020-12-02 05:56:32
【问题描述】:

我正在使用 djangorestframework 和 mysql 数据库。 我有一个基于搜索查询参数返回列表的视图。 我正在使用 rest_frameworks.filters SearchFilter 进行基于搜索的过滤。 这是我的看法:

from rest_framework import filters
from rest_framework.generics import ListAPIView
...

class FooListView(ListAPIView):
    serializer_class = SymbolSerializer
    queryset = Symbol.objects.all()
    filter_backends = [filters.SearchFilter]
    search_fields = ['field_A', 'field_B', 'field_C']

要调用的示例 URL 是:

http://localhost:8000/symbols/symbols/?search=bird

现在一切正常,但我需要一个 filters.SearchFilter 不支持的功能。 我希望我的搜索按 search_fields 的优先级排序。

例如这里有两条记录:

foo1 : {"field_A": "any", "field_B": "many", "field_C": "bar", "id": 3}

foo2 : {"field_A": "many", "field_B": "any", "field_C": "bar", "id": 4}

现在,当我使用 search='many' 参数进行搜索时,我希望视图返回一个列表,其中 foo2 记录高于 foo1(如 [foo2, foo1] ),因为我希望搜索的优先级是 field_A score 但它只返回一个按 id ([foo1, foo2]) 排序的列表。

有什么帮助吗?

【问题讨论】:

    标签: mysql django django-rest-framework django-filter django-rest-framework-filters


    【解决方案1】:

    我只是偶然发现了同样的问题。

    我的解决方案是使用来自this response 的灵感稍微调整 DRF 的filters.SearchFilter 的搜索逻辑,并最终得到以下自定义过滤器类:

    class PriorizedSearchFilter(filters.SearchFilter):
        def filter_queryset(self, request, queryset, view):
            """Override to return priorized results."""
            # Copy paste from DRF
            search_fields = getattr(view, 'search_fields', None)
            search_terms = self.get_search_terms(request)
    
            if not search_fields or not search_terms:
                return queryset
    
            orm_lookups = [
                self.construct_search(six.text_type(search_field))
                for search_field in search_fields
            ]
            base = queryset
            conditions = []
    
            # Will contain a queryset for each search term
            querysets = list()
    
            for search_term in search_terms:
                queries = [
                    models.Q(**{orm_lookup: search_term})
                    for orm_lookup in orm_lookups
                ]
    
                # Conditions for annotated priority value. Priority == inverse of the search field's index.
                # Example: 
                #   search_fields = ['field_A', 'field_B', 'field_C']
                #   Priorities are field_A = 2, field_B = 1, field_C = 0
                when_conditions = [models.When(queries[i], then=models.Value(len(queries) - i - 1)) for i in range(len(queries))]
                
                # Generate queryset result for this search term, with annotated priority
                querysets.append(
                    queryset.filter(reduce(operator.or_, queries))
                        .annotate(priority=models.Case(
                            *when_conditions,
                            output_field=models.IntegerField(),
                            default=models.Value(-1)) # Lowest possible priority
                        )
                    )
    
            # Intersect all querysets and order by highest priority
            queryset = reduce(operator.and_, querysets).order_by('-priority')
    
            # Copy paste from DRF
            if self.must_call_distinct(queryset, search_fields):
                # Filtering against a many-to-many field requires us to
                # call queryset.distinct() in order to avoid duplicate items
                # in the resulting queryset.
                # We try to avoid this if possible, for performance reasons.
                queryset = distinct(queryset, base)
            return queryset
    
    

    使用 filter_backends = [PrioritizedSearchFilter] 就可以了。

    【讨论】:

      猜你喜欢
      • 2022-06-13
      • 2011-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多