2010-03-17 18 views
8

Ich möchte eine Suchfunktion in einer Django-Blogging-Anwendung implementieren. Der Status quo ist, dass ich eine Liste von Zeichenfolgen habe, die vom Benutzer bereitgestellt werden, und das Abfrage-Set wird von jeder Zeichenfolge eingegrenzt, um nur die Objekte einzuschließen, die der Zeichenfolge entsprechen.Wie implementiert man die Volltextsuche in Django?

Siehe:

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') 

Nun, was ist, wenn ich will nicht jedes Wort do verketten, die durch eine logische gesucht und doch mit einem logischen ODER? Wie würde ich das tun? Gibt es eine Möglichkeit, dies mit Djangos eigenen Queryset-Methoden zu tun, oder muss man auf reine SQL-Abfragen zurückgreifen?

Im Allgemeinen ist es eine richtige Lösung, eine Volltextsuche wie diese zu machen, oder würden Sie eine Suchmaschine wie Solr, Whoosh oder Xapian empfehlen. Was sind ihre Vorteile?

Antwort

15

Ich empfehle Ihnen, eine Suchmaschine zu adoptieren.

Wir Haystack search verwendet haben, eine modulare Suchanwendung für django unterstützt viele Suchmaschinen (Solr, Xapian, Whoosh, etc ...)

Vorteile:

  • Faster
  • Suche durchführen Abfragen auch ohne Abfrage der Datenbank.
  • Highlight gesuchten Begriffe
  • "Mehr wie diese" Funktionalität
  • Rechtschreibung Vorschläge
  • besseres Ranking
  • etc ...

Nachteile:

  • Suchindizes wachsen in der Größe ziemlich schnell
  • Eines der besten Suchmaschinen (Solr) als Java-Servlet ausgeführt (Xapian nicht)

Wir sind ziemlich glücklich mit dieser Lösung und es ist ziemlich einfach zu implementieren.

2

Für die Volltextsuche in Python, siehe PyLucene. Es ermöglicht sehr komplexe Abfragen. Das Hauptproblem hier ist, dass Sie einen Weg finden müssen, um Ihrer Suchmaschine mitzuteilen, welche Seiten sich geändert haben und den Index schließlich aktualisieren.

Alternativ können Sie Google Sitemaps verwenden, damit Google Ihre Website schneller indiziert und anschließend ein benutzerdefiniertes Abfragefeld in Ihre Site einbindet. Der Vorteil ist, dass Sie Google nur die geänderten Seiten mitteilen müssen und Google die ganze Arbeit erledigt (Indexierung, Analyse der Abfragen usw.). Darüber hinaus sind die meisten Menschen gewohnt, Google für die Suche zu verwenden. Außerdem wird Ihre Website auch bei den globalen Google-Suchanfragen aktuell bleiben.

5

Eigentlich ist die Abfrage, die Sie geschrieben haben tut Verwendung oder besser gesagt als UND - Sie \ verwenden die Q Objekte zu trennen. UND wäre &.

Im Allgemeinen würde ich sehr empfehlen, eine richtige Suchmaschine zu verwenden. Wir hatten einen guten Erfolg mit Haystack an der Spitze von Solr - Haystack verwaltet die gesamte Solr-Konfiguration und legt eine nette API offen, die sehr ähnlich zu Djangos eigenem ORM ist.

+0

Er sollte OR verwenden, wenn er eine Volltextsuche möchte. Sein Code ist klar, aber die Beschreibung ist ein bisschen verwirrend. –

+0

Aber die .filter() -Anweisungen sind AND'ed zusammen, nicht wahr? Daher wird nach jedem Suchargument (Wort) im Titel des Posts ODER nach dem Inhalt ODER in den zugehörigen Tags gesucht. Um jedoch als Ergebnis angezeigt zu werden, muss ein Beitrag alle Suchargumente in seinem OR- oder Inhalt-OR-Tag enthalten. Das ist in Ordnung und genau das, was ich erreichen wollte. Ich bin nur neugierig darauf, wie man implementiert, dass ein Ergebnis nur eines der Wörter benötigt, die in einem seiner Attribute vorhanden sein müssen (nicht alle). – jnns

4

Antwort auf Ihre allgemeine Frage: Verwenden Sie unbedingt eine ordnungsgemäße Anwendung dafür.

Mit Ihrer Anfrage untersuchen Sie immer den gesamten Inhalt der Felder (Titel, Text, Tags). Sie erhalten keinen Nutzen von Indizes, etc.

Mit einer geeigneten Volltext-Suchmaschine (oder wie immer Sie es nennen), Text (Wörter) wird jedes Mal, wenn Sie neue Datensätze einfügen, indexiert. So werden Abfragen viel schneller, besonders wenn Ihre Datenbank wächst.

4

SOLR ist sehr einfach einzurichten und mit Django zu integrieren. Heuhaufen macht es noch einfacher.

2

Ich denke, Volltextsuche auf einer Anwendungsebene ist eher eine Frage dessen, was Sie haben und wie Sie erwarten, dass es skaliert. Wenn Sie eine kleine Website mit geringer Nutzung betreiben, ist es meiner Meinung nach günstiger, eine benutzerdefinierte Volltextsuche durchzuführen, anstatt eine Anwendung zu installieren, um die Suche für Sie durchzuführen. Und die Anwendung würde beim Speichern von Daten mehr Abhängigkeit, Wartung und zusätzlichen Aufwand erzeugen. Indem Sie Ihre Suche selbst durchführen und nette benutzerdefinierte Funktionen hinzufügen. Wenn Ihr Text beispielsweise genau einem Titel entspricht, können Sie den Nutzer auf diese Seite verweisen, anstatt die Ergebnisse anzuzeigen. Eine andere wäre, Titel: oder Autor: Präfixen zu Schlüsselwörtern zu erlauben.

Hier ist eine Methode, die ich verwendet habe, um relevante Suchergebnisse aus einer Web-Abfrage zu generieren.

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)) 
Verwandte Themen