2010-03-17 12 views
8

Me gustaría implementar una función de búsqueda en una aplicación de blog de django. El status quo es que tengo una lista de cadenas suministradas por el usuario y el conjunto de consultas se reduce por cada cadena para incluir solo aquellos objetos que coinciden con la cadena.¿Cómo implementar la búsqueda de texto completo en Django?

Ver:

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

Ahora bien, ¿y si yo no quería hacer concatenar cada palabra que se busca por un AND lógico, pero con un OR lógico? ¿Como podría hacerlo? ¿Hay alguna manera de hacerlo con los propios métodos Queryset de Django o uno tiene que recurrir a consultas SQL sin formato?

En general, ¿es una solución adecuada para hacer búsquedas de texto completo como esta o recomendaría utilizar un motor de búsqueda como Solr, Whoosh o Xapian? ¿Cuáles son sus beneficios?

Respuesta

15

Le sugiero que adopte un motor de búsqueda.

Hemos utilizado Haystack search, una aplicación de búsqueda de apoyo modular para Django muchos motores de búsqueda (Solr, Xapian, Whoosh, etc ...)

Ventajas:

  • más rápidos
  • realizar la búsqueda consultas incluso sin consultar la base de datos.
  • resaltar los términos buscados
  • "productos de la misma" funcionalidad
  • sugerencias de ortografía
  • mejor ranking
  • etc ...

Desventajas:

  • índices de búsqueda pueden crecer en tamaño bastante rápido
  • Uno de los mejores motores de búsqueda (Solr) se ejecutan como un servlet de Java (Xapian no lo hace)

estamos bastante contentos con esta solución y que es bastante fácil de implementar.

2

Para buscar texto completo en Python, mira PyLucene. Permite consultas muy complejas. El principal problema aquí es que debe encontrar una forma de decirle a su motor de búsqueda qué páginas cambiaron y actualizar el índice con el tiempo.

O bien, puede usar Google Sitemaps para indicarle a Google que indexe su sitio más rápido y luego incrustar un campo de consulta personalizado en su sitio. La ventaja aquí es que solo necesita decirle a Google las páginas modificadas y Google hará todo el trabajo (indexación, análisis de las consultas, etc.). Además de eso, la mayoría de la gente está acostumbrada a usar Google para buscar y además mantendrá su sitio al día en las búsquedas globales de Google.

5

En realidad, la consulta que ha publicado hace uso O en lugar de Y - que está utilizando \ para separar los objetos Q. Y sería &.

En general, recomendaría encarecidamente el uso de un motor de búsqueda adecuado. Hemos tenido un gran éxito con Haystack en la parte superior de Solr. Haystack administra toda la configuración de Solr y expone una buena API muy similar al propio ORM de Django.

+0

Debería estar usando O si desea una búsqueda de texto completo. Su código es claro, pero la descripción es un poco confusa. –

+0

Pero las declaraciones .filter() están AND 'juntas, ¿no es así? Por lo tanto, cada argumento de búsqueda (palabra) se busca en el título de la publicación O en el contenido O en las etiquetas relacionadas. Pero para aparecer como resultado, una publicación debe tener todos los argumentos de búsqueda en su título OR contenido O etiqueta. Eso está bien y es exactamente lo que quería lograr. Solo tengo curiosidad sobre cómo implementar que un resultado necesita solo una de las palabras para estar presente en uno de sus atributos (no en todos). – jnns

4

Respuesta a su general pregunta: Use definitivamente una aplicación adecuada para esto.

Con su consulta, usted siempre examina todo el contenido de los campos (título, texto, etiquetas). No obtiene ningún beneficio de los índices, etc.

Con un motor de búsqueda de texto completo adecuado (o como se llame), texto (palabras) está indexado cada vez que inserta registros nuevos. Por lo tanto, las consultas serán mucho más rápidas, especialmente cuando su base de datos crezca.

4

SOLR es muy fácil de configurar e integrar con Django. Haystack lo hace aún más simple.

2

Creo que la búsqueda de texto completo en un nivel de aplicación es más una cuestión de lo que tiene y cómo espera que se escale. Si ejecuta un sitio pequeño con poco uso, creo que podría ser más económico dedicar algún tiempo a realizar una búsqueda personalizada de texto completo en lugar de instalar una aplicación para realizar la búsqueda. Y la aplicación crearía más dependencia, mantenimiento y esfuerzo adicional al almacenar datos. Al hacer su búsqueda usted mismo y puede construir en agradables características personalizadas. Como por ejemplo, si el texto coincide exactamente con un título, puede dirigir al usuario a esa página en lugar de mostrar los resultados. Otro sería permitir el título: o el autor: los prefijos a las palabras clave.

Este es un método que he usado para generar resultados de búsqueda relevantes a partir de una consulta 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)) 
Cuestiones relacionadas