如何按搜索词出现位置排序Django QuerySet实现菜单自动补全?
解决方案:按搜索词位置优先排序自动补全结果
要实现优先按搜索词在菜品名称中的出现位置排序,再按字母顺序排序的需求,你需要借助数据库的字符串位置计算函数,在Django中通过annotate添加临时排序字段,再基于该字段排序。
1. 选择对应数据库的位置计算函数
不同数据库的字符串位置函数不同,需根据你的数据库类型选择:
- MySQL/MariaDB:使用
CharIndex函数 - PostgreSQL:使用
StrPos函数 - SQLite:使用
Instr函数
2. 修改Django视图代码
以下是适配不同数据库的完整代码:
针对MySQL/MariaDB
from django.db.models import Value from django.db.models.functions import CharIndex, Lower def load_menu(request): term = request.GET['term'] # 转小写以匹配icontains的大小写不敏感查询 lower_term = Value(term.lower()) menu = Menu.objects.filter(item__icontains=term)\ .annotate(term_position=CharIndex(lower_term, Lower('item')))\ .order_by('term_position', 'item', 'id') data = [] for item in menu: data.append({ 'label': item.item, 'value': item.id, }) return JsonResponse({'data': data})
针对PostgreSQL
from django.db.models import Value from django.db.models.functions import StrPos, Lower def load_menu(request): term = request.GET['term'] lower_term = Value(term.lower()) menu = Menu.objects.filter(item__icontains=term)\ .annotate(term_position=StrPos(Lower('item'), lower_term))\ .order_by('term_position', 'item', 'id') data = [] for item in menu: data.append({ 'label': item.item, 'value': item.id, }) return JsonResponse({'data': data})
针对SQLite
from django.db.models import Value from django.db.models.functions import Instr, Lower def load_menu(request): term = request.GET['term'] lower_term = Value(term.lower()) menu = Menu.objects.filter(item__icontains=term)\ .annotate(term_position=Instr(Lower('item'), lower_term))\ .order_by('term_position', 'item', 'id') data = [] for item in menu: data.append({ 'label': item.item, 'value': item.id, }) return JsonResponse({'data': data})
代码说明
annotate(term_position=...):添加临时字段term_position,存储搜索词在菜品名称(转小写后)中的起始位置,位置数值越小代表搜索词出现得越靠前。order_by('term_position', 'item', 'id'):先按term_position升序排序(优先展示搜索词出现位置靠前的结果),再按菜品名称字母序排序,最后按id排序保证结果的唯一性。- 转小写处理:保证大小写不敏感的查询和位置计算一致,避免因大小写差异导致排序错误。
内容的提问来源于stack exchange,提问作者Thomas Huitema
相关产品推荐
相关产品推荐

