2010-09-20 3 views
0

У меня есть три входа, поступающих из формы. Это name, neighborhoods и tags. Окрестности и теги - это многострочные списки строк. Вот мой текущий запрос:Взвешенный поиск в Django

q = Restaurant.objects.filter(name__icontains=name) 
q = q.filter(neighborhoods__name__in=neighborhoods) 
for tag in tags: 
    q = q.filter(tags__name=tag) 
q = q.order_by('name').distinct() 

который в настоящее время получает все рестораны, которые имеют все теги и все из окрестностей. У меня есть небольшая проблема, сделав это взвешенным поиском. В принципе, для каждого совпадающего тега и окрестности я хочу добавить точку в столбец веса. Затем я закажу по весу, и даже если ресторан будет соответствовать только двум из трех тегов, он будет показываться (его вес будет 2). Это делается для того, чтобы не допустить 0 результатов и показать, насколько это возможно. Кроме того, я хочу, чтобы для выбора ресторана требовалось не менее 1 балла.

Я думаю, в SQL было бы что-то вроде:

SELECT *, 
    (SELECT COUNT(1) 
    FROM tags t 
    WHERE t.name IN (%s) 
    ) AS weight 
FROM restaurants 
WHERE weight > 0 
ORDER BY weight DESC 

ответ

2

Вы хотите использовать annotate()

from django.db.models import Count 
q = Restaurant.objects.filter(name__icontains=name) 
q = q.filter(neighborhoods__name__in=neighborhoods) 
for tag in tags: 
    q = q.filter(tags__name=tag) 
q = q.order_by('name').annotate(num_tags=Count('tags__name')).filter(num_tags__gte=2) 

обновления

Глядя на код еще раз я вижу, что, к сожалению, это отфильтровывая так, чтобы работали только совпадения со всеми тегами. Я думаю, что именно это изменение должно работать:

Избавьтесь от:

for tag in tags: 
    q = q.filter(tags__name=tag) 

Заменить:

q = q.filter(tags__name__in=tags) 

Таким образом, он соответствует всем запросам, где ресторан помечен по крайней мере, один из запрошенные метки. Затем annotate и filter позаботятся о том, чтобы убедиться, что он соответствует не менее 2.

+0

Подождите минуту здесь ... это просто подсчет количества тегов для ресторана, а не # тегов, соответствующих списку тегов. Мне нужно что-то вроде '.annotate (num_tags = Count ('tags__name__in' = tags)) –

+1

Нет, код' q = q.filter (tags__name__in = tags) 'ограничивает строки только тегами, которые соответствуют. '.annotate (Count ('tags__name'))' группирует записи по всем, кроме записи тега. Если вы ищете 5 тегов, у вас есть 3 ресторана, A, B и C; и A соответствует 1 тегу, B 3 и C 4, а затем после аннотированных строк (A: num_tags 1, B: num_tags 3, C: num_tags 4) и 'filter()' удаляет A, оставляя B и C. Я могу скажите, что '.annotate (num_tags = Count ('tags__name__in' = tags))' будет выдавать ошибку. Аргумент для 'Count' - это просто имя поля, а не предложение, как в фильтре. –

+0

ОК, мой плохой. Спасибо огромное! –

Смежные вопросы