Как реализовать полнотекстовый поиск в Django?

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

посмотреть:

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. Каковы их преимущества?

6 ответов


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

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

плюсы:

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

недостатки:

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

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


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

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


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

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

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


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


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

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


Я думаю, что полнотекстовый поиск на уровне приложения больше зависит от того, что у вас есть и как вы ожидаете его масштабирования. Если вы запускаете небольшой сайт с низким использованием, я думаю, что было бы более доступным потратить некоторое время на создание пользовательского полнотекстового поиска, а не на установку приложения для выполнения поиска для вас. И приложение создаст больше зависимости, обслуживания и дополнительных усилий при хранении данных. Сделав свой поиск самостоятельно, вы можете создавать приятные пользовательские функции. Как например, если ваш текст точно соответствует одному заголовку, вы можете направить пользователя на эту страницу вместо отображения результатов. Другим было бы разрешить 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))