Как реализовать полнотекстовый поиск в 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')

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

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


person jnns    schedule 17.03.2010    source источник


Ответы (6)


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

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

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

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

Недостатки:

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

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

person Andrea Zilio    schedule 17.03.2010

Фактически, отправленный вами запрос действительно использует ИЛИ, а не И - вы используете \ для разделения объектов Q. И будет &.

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

person Daniel Roseman    schedule 17.03.2010
comment
Он должен использовать ИЛИ, если ему нужен полнотекстовый поиск. Его код ясен, но описание немного сбивает с толку. - person Stefan Lundström; 17.03.2010
comment
Но операторы .filter () объединены вместе, не так ли? Таким образом, каждый аргумент поиска (слово) ищется в заголовке сообщения ИЛИ в содержании ИЛИ в связанных тегах. Но чтобы появиться в результате, сообщение должно содержать все аргументы поиска в заголовке ИЛИ в теге содержимого ИЛИ. Это нормально, и именно этого я хотел достичь. Мне просто любопытно, как реализовать, что результату нужно, чтобы только одно из слов присутствовало в одном из их атрибутов (не во всех). - person jnns; 17.03.2010

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

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

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

person Felix Kling    schedule 17.03.2010

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

person Sriram    schedule 17.03.2010

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

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

person Aaron Digulla    schedule 17.03.2010

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

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

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))
person Stefan Lundström    schedule 17.03.2010