Django + haystack + elasticsearch простой пример

Эта статья вчера называлась 'Django + haystack + whoosh простой пример'.

Но после того как я попытался использовать эту связку на amvhub.com, оказалось, что whoosh имеет некоторые неприятные особенности:

Тикет в whoosh issues tracker: https://bitbucket.org/mchaput/whoosh/issue/97/search-index-contains-a-lot-of-duplicates.

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

Это тестовый проект, который я создал для экспериментов с haystack перед тем как использовать его в других проектах https://bitbucket.org/nanvel/hstest/

Я использовал haystack и elasticsearch для реализации поиска по заметкам.

Класс модели с заметками:

class Note(models.Model):
    title = models.CharField(max_length=1000)
    body = models.TextField()
    timestamp = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.title

Дальше я покажу несколько простых шагов которые помогут реализовать функцию поиска.

1. Требования

pip install django-haystack==2.0.0
pip install pyelasticsearch==0.5

Установка elasticsearch на Mac:

brew install elasticsearch
# and launch:
elasticsearch -f -D es.config=/usr/local/Cellar/elasticsearch/0.90.2/config/elasticsearch.yml

Установка elasticsearch на Ubuntu 12.04:

sudo apt-get update
sudo apt-get install openjdk-7-jre-headless -y
wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.0.deb
sudo dpkg -i elasticsearch-0.90.0.deb

2. Изменим settings.py:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',

    'south',
    'haystack',

    'hstest.apps.notes',
)

HAYSTACK_CONNECTIONS = {
    'default': {
        'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
        'URL': 'http://127.0.0.1:9200/',
        'INDEX_NAME': 'haystack',
    },
}

HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

Последняя строка включает сигнальный процессор, который при каждом изменении в модели, запустит updateindex. https://django-haystack.readthedocs.org/en/latest/signalprocessors.html#realtime-realtimesignalprocessor

3. Создадим search_indexes.py:

from haystack import indexes  
from django.utils import timezone
from .models import Note


class NoteIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    title = indexes.CharField(model_attr='title')
    body = indexes.CharField(model_attr='body')

    def get_model(self):
        return Note

    def index_queryset(self, using=None):
        """Used when the entire index for model is updated."""
        return self.get_model().objects.filter(timestamp__lte=timezone.now())

Этот шаг немного запутал меня.

Поле text здесь самое важное. Все что вы хотите сделать доступным для поиска должно быть тут.

Я хочу сделать поиск по Note.title и Note.body, чтобы добавить их в поле text, нужно отредактировать notes_text.txt

Давайте создадим его.

templates/search/indexes/notes/note_text.txt

{{ object.title }}
{{ object.body }}

Тогда зачем нам оставшиеся поля?

Они нужны для представления результатов поиска. Если title поля не будет, results[n].title вызовет исключение.

4. Используем haystack forms или views: http://django-haystack.readthedocs.org/en/latest/viewsandforms.html

Я думаю, что формы являются более гибкими, поэтому этот пример будет использовать SearchForm.

Эта форма принимает запрос из request.GET ['Q'].

SearchForm по умолчанию вернет пустой ответ если запрос (request.GET['Q']) пустой, это не устраивало меня и я переопределил форму.

forms.py

from haystack.forms import SearchForm


class NotesSearchForm(SearchForm):

    def no_query_found(self):
        return self.searchqueryset.all()

views.py

from django.shortcuts import render_to_response

from .forms import NotesSearchForm


def notes(request):
    form = NotesSearchForm(request.GET)
    notes = form.search()
    return render_to_response('notes.html', {'notes': notes})

5. И добавим простую форму в шаблон:

{% extends 'base.html' %}

{% block content %}
<form type="get" action=".">
    <input type="text" name="q">
    <button type="submit">Search</button>
</form>

{% for note in notes %}
<h1>{{ note.title }}</h1>
<p>
    {{ note.body }}
</p>
{% endfor %}
{% endblock %}

6. Перед использованием поиска мы должны создать индекс:

python manage.py rebuild_index

Каждый раз когда данные были изменены, мы должны обновлять индекс:

python manage.py update_index

Но это не обязательно, если мы используем RealtimeSignalProcessor.

Удачи!

Ссылки: http://django-haystack.readthedocs.org/en/latest/tutorial.html

См. также: [haystack] RealtimeSignalProcessor doesn't take into account an index_queryset

Оригинал: статья статья в блоге nanvel.name.

UPD! - по мотивам http://habrahabr.ru/sandbox/82755/

Сегодня залочили сервер за большой исходящий трафик. Если вы открыли уже ссылку строчкой выше то вы уже догадались откуда взялся этот трафик.

Лечение:

Редактируем файл /etc/elasticsearch/elasticsearch.yml

network.host: 127.0.0.1

Перезапускаем elasticsearch

/etc/init.d/elasticsearch restart

comments powered by Disqus

Об авторе

igorakintev@ya.ru

Яндекс.Метрика