2010-03-17 6 views
8

Я хотел бы реализовать функцию поиска в приложении для ведения блога django. Статус-кво заключается в том, что у меня есть список строк, предоставленных пользователем, и набор запросов сужается каждой строкой, чтобы включать только те объекты, которые соответствуют строке.Как реализовать полнотекстовый поиск в Django?

См:

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

Теперь, что если я не хочу делать конкатенации каждое слово, которое ищется на логическое И, но с логическим ИЛИ? Как мне это сделать? Есть ли способ сделать это с помощью собственных методов Queryset Django или нужно вернуться к необработанным SQL-запросам?

В общем, это правильное решение для полнотекстового поиска, подобного этому, или вы бы рекомендовали использовать поисковую систему, такую ​​как Solr, Whoosh или Xapian. Каковы их преимущества?

ответ

15

Я предлагаю вам принять поисковую систему.

Мы использовали Haystack search, модульное приложение для поиска Джанго, поддерживающего множество поисковых систем (Solr, Xapian, Свист и т.д. ...)

Преимущества:

  • Быстрее
  • выполнять поиск запросов даже без запроса базы данных.
  • Highlight искали термины
  • "Больше как это" функциональность
  • Правописание предложения
  • Лучше ранжирование
  • и т.д ...

Недостатки:

  • поиска Индексы могут расти в размерах довольно быстро
  • Одна из лучших поисковых систем (Solr) работает как сервлет Java (Xapian не делает)

Мы очень довольны этим решением, и его довольно легко реализовать.

2

Для полнотекстового поиска в Python см. PyLucene. Это позволяет очень сложные запросы. Основная проблема здесь заключается в том, что вы должны найти способ сообщить своей поисковой системе, какие страницы были изменены и в конечном итоге обновить индекс.

В качестве альтернативы вы можете использовать Google Sitemaps, чтобы сообщить Google, чтобы быстрее индексировать ваш сайт, а затем встраивать пользовательское поле запроса на свой сайт. Преимущество здесь в том, что вам просто нужно сообщить Google об измененных страницах, и Google сделает всю тяжелую работу (индексирование, разбор запросов и т. Д.). Кроме того, большинство людей привыкли использовать Google для поиска, и он также будет поддерживать ваш сайт в глобальном поиске Google.

5

На самом деле, запрос вы выложили делает использование OR вместо AND - вы используете \ отделить Q объекты. И будет &.

В целом, я бы очень рекомендовал использовать подходящую поисковую систему. У нас был хороший успех с Haystack поверх Solr - Haystack управляет всей конфигурацией Solr и предоставляет хороший API, очень похожий на собственный ORM Django.

+0

Он должен использовать ИЛИ, если ему нужен полный текстовый поиск. Его код ясен, но описание немного запутанно. –

+0

Но утверждения .filter() AND'ed вместе, не так ли? Таким образом, каждый аргумент поиска (слово) выполняется в заголовке сообщения или в содержимом ИЛИ в связанных тегах. Но чтобы показать результат, сообщение должно содержать все аргументы поиска в его заголовке ИЛИ теге ИЛИ ИЛИ. Это прекрасно и именно то, чего я хотел достичь. Мне просто интересно, как реализовать то, что результат нуждается только в одном из слов, присутствующих в одном из их атрибутов (не для всех). – jnns

4

Ответ на ваш вопрос общий вопрос: Определенно используйте правильное приложение для этого.

По вашему запросу вы всегда проверяете содержимое полей (название, текст, метки). Вы не получаете никакой выгоды от индексов и т. Д.

С надлежащей полнотекстовой поисковой системой (или как бы там вы ее называете) текст (слова) индексируется каждый раз, когда вы вставляете новые записи. Таким образом, запросы будут намного быстрее, особенно когда ваша база данных будет расти.

4

SOLR очень прост в настройке и интеграции с Django. Haystack делает его еще проще.

2

Я думаю, что полнотекстовый поиск на уровне приложения больше зависит от того, что у вас есть и как вы ожидаете его масштабирования. Если вы запустите небольшой сайт с низким уровнем использования, я думаю, что было бы более целесообразным положить некоторое время на создание пользовательского полнотекстового поиска, а не на установку приложения для выполнения поиска для вас. И приложение будет создавать большую зависимость, обслуживание и дополнительные усилия при хранении данных. Сделав поиск самостоятельно, вы сможете создавать приятные пользовательские функции. Например, если ваш текст точно соответствует одному заголовку, вы можете направить пользователя на эту страницу, а не показывать результаты. Другой вариант - разрешить заголовок: или автор: префиксы для ключевых слов.

Вот метод, который я использовал для создания релевантных результатов поиска из веб-запроса.

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