2010-03-17 2 views
8

Je voudrais implémenter une fonction de recherche dans une application de blogging django. Le statu quo est que j'ai une liste de chaînes fournies par l'utilisateur et le jeu de requête est réduit par chaque chaîne pour inclure uniquement les objets qui correspondent à la chaîne.Comment implémenter la recherche en texte intégral dans Django?

Voir:

if request.method == "POST": 
    form = SearchForm(request.POST) 
    if form.is_valid(): 
     posts = Post.objects.all() 
     for string in form.cleaned_data['query'].split(): 
      posts = posts.filter(
        Q(title__icontains=string) | 
        Q(text__icontains=string) | 
        Q(tags__name__exact=string) 
        ) 
     return archive_index(request, queryset=posts, date_field='date') 

Maintenant, si je ne voulais pas chaque mot do concaténer qui est recherché par un ET logique mais avec un OU logique? Comment ferais-je cela? Existe-t-il un moyen de faire cela avec les méthodes Queryset de Django ou faut-il revenir aux requêtes SQL brutes?

En général, est-ce une bonne solution de faire une recherche de texte intégral comme ceci ou recommanderiez-vous d'utiliser un moteur de recherche comme Solr, Whoosh ou Xapian. Quels sont leurs avantages?

Répondre

15

Je vous propose d'adopter un moteur de recherche.

Nous avons utilisé Haystack search, une application de recherche modulaire pour django supportant de nombreux moteurs de recherche (Solr, xapian, Whoosh, etc ...)

Avantages:

  • plus rapide
  • effectuer une recherche requêtes même sans interroger la base de données.
  • Sélectionnez termes recherchés
  • "Plus comme cette" fonctionnalité
  • suggestions d'orthographe
  • Meilleure classement
  • etc ...

Inconvénients:

  • Index de recherche peuvent se développer en taille assez rapide
  • L'un des meilleurs moteurs de recherche (Solr) exécuté comme une servlet Java (Xapian ne)

Nous sommes très satisfaits de cette solution et il est assez facile à mettre en œuvre.

2

Pour la recherche en texte intégral en Python, regardez PyLucene. Cela permet des requêtes très complexes. Le principal problème ici est que vous devez trouver un moyen de dire à votre moteur de recherche quelles pages ont changé et mettre à jour l'index éventuellement.

Vous pouvez également utiliser Google Sitemaps pour indiquer à Google d'indexer votre site plus rapidement, puis d'intégrer un champ de requête personnalisé dans votre site. L'avantage ici est que vous avez juste besoin de dire à Google les pages modifiées et Google fera tout le travail (indexation, analyse des requêtes, etc.). En plus de cela, la plupart des gens sont habitués à utiliser Google pour effectuer des recherches. De plus, votre site restera à jour dans les recherches Google mondiales.

5

En fait, la requête que vous avez posté ne fait utiliser ou plutôt que ET - vous utilisez \ pour séparer les Q objets. ET serait &.

En général, je recommande fortement d'utiliser un moteur de recherche approprié. Nous avons eu un bon succès avec Haystack au dessus de Solr - Haystack gère toute la configuration de Solr, et expose une belle API très similaire à l'ORM de Django.

+0

Il devrait utiliser OU s'il veut une recherche en texte intégral. Son code est clair, mais la description est un peu confuse. –

+0

Mais les instructions .filter() sont ET'ed ensemble, n'est-ce pas? Ainsi, chaque argument de recherche (mot) est recherché dans le titre de la publication OU le contenu OU dans les étiquettes associées. Mais pour apparaître comme résultat, un post doit avoir tous les arguments de recherche dans son titre OU son contenu OR tag. C'est bien et c'est exactement ce que je voulais accomplir. Je suis juste curieux de savoir comment implémenter qu'un résultat n'a besoin que d'un des mots pour être présent dans l'un de ses attributs (pas tous). – jnns

4

réponse à votre général question: Utilisez certainement une application appropriée pour cela.

Avec votre requête, vous toujours examiner tout le contenu des champs (titre, texte, tags). Avec un bon moteur de recherche de texte intégral (ou peu importe comment vous l'appelez), le texte (mots) est indexé chaque fois que vous insérez de nouveaux enregistrements. Les requêtes seront donc beaucoup plus rapides, surtout si votre base de données se développe.

4

SOLR est très facile à installer et à intégrer avec Django. Haystack le rend encore plus simple.

2

Je pense que la recherche en texte intégral au niveau de l'application dépend davantage de ce que vous avez et de la façon dont vous vous attendez à ce qu'elle évolue. Si vous utilisez un petit site avec une faible utilisation, je pense qu'il pourrait être plus économique de consacrer du temps à faire une recherche en texte intégral personnalisée plutôt que d'installer une application pour effectuer la recherche pour vous. Et l'application créerait plus de dépendance, de maintenance et d'effort supplémentaire lors du stockage des données. En faisant votre recherche vous-même et vous pouvez construire de belles fonctionnalités personnalisées. Comme par exemple, si votre texte correspond exactement à un titre, vous pouvez diriger l'utilisateur vers cette page au lieu de montrer les résultats. Un autre serait d'autoriser title: ou author: prefixes à des mots-clés.

Voici une méthode que j'ai utilisée pour générer des résultats de recherche pertinents à partir d'une requête Web.

import shlex 

class WeightedGroup: 
    def __init__(self): 
     # using a dictionary will make the results not paginate 
     # but it will be a lot faster when storing data   
     self.data = {} 

    def list(self, max_len=0): 
     # returns a sorted list of the items with heaviest weight first 
     res = [] 
     while len(self.data) != 0: 
      nominated_weight = 0      
      for item, weight in self.data.iteritems(): 
       if weight > nominated_weight: 
        nominated = item 
        nominated_weight = weight 
      self.data.pop(nominated) 
      res.append(nominated) 
      if len(res) == max_len: 
       return res 
     return res 

    def append(self, weight, item): 
     if item in self.data: 
      self.data[item] += weight 
     else: 
      self.data[item] = weight 


def search(searchtext): 
    candidates = WeightedGroup() 

    for arg in shlex.split(searchtext): # shlex understand quotes 

     # Search TITLE 
     # order by date so we get most recent posts 
     query = Post.objects.filter_by(title__icontains=arg).order_by('-date') 
     arg_hits = query.count() # count is cheap 

     if arg_hits > 1000: 
      continue # skip keywords which has too many hits 

     # Each of these are expensive as it would transfer data 
     # from the db and build a python object, 
     for post in query[:50]: # so we limit it to 50 for example     
      # more hits a keyword has the lesser it's relevant 
      candidates.append(100.0/arg_hits, post.post_id) 

     # TODO add searchs for other areas 
     # Weight might also be adjusted with number of hits within the text 
     # or perhaps you can find other metrics to value an post higher, 
     # like number of views 

    # candidates can contain a lot of stuff now, show most relevant only 
    sorted_result = Post.objects.filter_by(post_id__in=candidates.list(20)) 
Questions connexes