2010-09-20 4 views
0

J'ai trois entrées provenant d'un formulaire. Ils sont name, neighborhoods et tags. Les quartiers et les tags sont des listes de chaînes de boîtes multi-sélection. Voici ma requête en cours:Recherche pondérée dans 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() 

qui va chercher actuellement tous les restaurants qui ont toutes les balises et tous les quartiers. J'ai un peu de mal à faire une recherche pondérée. Fondamentalement, pour chaque étiquette et le voisinage qui correspond, je veux ajouter un point à une colonne de poids. Ensuite, je vais commander au poids et même si un restaurant correspond seulement à deux des trois étiquettes, il sera toujours montré (son poids serait de 2). Ceci afin d'empêcher 0 résultats et de montrer le plus proche possible. De plus, je veux exiger qu'au moins 1 point soit requis pour choisir un restaurant.

je suppose que dans SQL ce serait quelque chose comme:

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

Répondre

2

Vous voulez utiliser 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) 

mise à jour

En regardant à nouveau le code, je vois que, malheureusement, il est filtrer de sorte que seules les correspondances avec tous les tags fonctionnent. Je pense que ce changement devrait fonctionner:

Débarrassez-vous de:

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

Remplacer par:

q = q.filter(tags__name__in=tags) 

De cette façon, elle correspond à toutes les requêtes où un restaurant est étiquetée avec au moins un des étiquettes demandées. Le annotate et le filter s'assurent plus tard qu'il correspond à au moins 2.

+0

Attendez une minute ici ... c'est juste le nombre de tags pour le restaurant, pas le nombre de tags qui correspondent à la liste de tags. J'ai besoin de quelque chose comme '.annotate (num_tags = Count ('tags__name__in' = tags))' –

+1

Non, le code 'q = q.filter (tags__name__in = tags)' limite les lignes à inclure uniquement les balises correspondantes. '.annotate (Count ('tags__name'))' regroupe les enregistrements par tout autre chose que l'enregistrement de variable. Si vous recherchez 5 balises, ayez 3 restaurants, A, B et C; et A correspond à 1 tag, B 3 et C 4, puis après les lignes d'annotation sont (A: num_tags 1, B: num_tags 3, C: num_tags 4) et 'filter()' supprime A, laissant B et C. Je peux vous dire que '.annotate (num_tags = Count ('tags__name__in' = tags))' lancerait une erreur. L'argument pour Count est juste le nom du champ, pas une clause comme dans le filtre. –

+0

ok, mon mauvais. Merci beaucoup! –

Questions connexes