Django ElasticSearch DRF 自动补全接口返回重复、无查询结果问题
问题原因
- 无参数也返回结果:
SuggesterFilterBackend本身没有内置空查询参数拦截逻辑,当传入的查询参数为空时,Elasticsearch Completion Suggester会将空字符串作为前缀,匹配索引内所有符合条件的补全候选,直接返回结果。 skip_duplicates=True不生效:该参数是Elasticsearch原生提供的配置,仅能过滤同一个文档在不同分片返回的重复建议项,无法过滤不同文档之间同字段值的重复内容。你的场景是不同商品文档存储了重复的标题,属于跨文档重复,该配置完全不生效。- 额外注意:你代码里的序列化类拼写错误,
ProdcutTitleSerializer应为ProductTitleSerializer,该问题不影响去重逻辑,但会导致接口正常序列化时报错。
修复方案
方案1:视图层改造(改造成本最低,适合中小数据量场景)
直接在视图类里增加空参数拦截,同时对ES返回的建议结果做内存去重,不需要调整索引结构,代码改动量最小。
参考实现代码:
from rest_framework.response import Response from django_elasticsearch_dsl_drf.filter_backends import SuggesterFilterBackend from django_elasticsearch_dsl_drf.viewsets import DocumentViewSet from django_elasticsearch_dsl_drf.constants import SUGGESTER_COMPLETION from users.paginations import LotPagination class SuggestionsAPIView(DocumentViewSet): document = ProductDocument serializer_class = ProductTitleSerializer # 修正拼写错误 filter_backends = [SuggesterFilterBackend] suggester_fields = { 'title': { 'field': 'title', 'suggesters': [SUGGESTER_COMPLETION], 'options': { # 多取部分结果,避免去重后返回数量不足 'size': 100, 'skip_duplicates': True, }, }, } def list(self, request, *args, **kwargs): # 拦截空参数请求,直接返回空结果 search_keyword = request.query_params.get("title", "").strip() if not search_keyword: return Response([]) # 获取ES原生返回的建议结果 base_resp = super().list(request, *args, **kwargs) suggest_result = base_resp.data.get("suggest", {}).get("title__completion", []) if not suggest_result: return Response([]) # 对建议项按标题文本去重,取够20条即返回 title_set = set() final_suggestions = [] for item in suggest_result[0].get("options", []): title_text = item["text"] if title_text not in title_set: title_set.add(title_text) final_suggestions.append(item) if len(final_suggestions) >= 20: break return Response(final_suggestions)
方案2:独立补全索引(性能最优,适合大数据量高并发场景)
单独创建一个仅存储不重复商品标题的补全索引,不直接复用商品主文档的completion字段。通过定时任务或数据库信号同步,将所有去重后的商品标题写入该独立索引,从数据源层面杜绝重复值,不需要在接口请求时做内存去重,性能更高。
注意该方案仍然需要在视图层保留空参数拦截逻辑,避免无查询参数时返回全量数据。
内容的提问来源于stack exchange,提问作者user14880125
相关产品推荐
相关产品推荐

