2015-01-06 3 views
0

Я использовал для поиска django, haystack и elasticsearch.Поиск нескольких слов elasticsearch haystack

Мой search_index.py:

from haystack import indexes 
from models import Advertisement 



class AdvertisementIndex(indexes.SearchIndex, indexes.Indexable): 
    text = indexes.CharField(document=True, use_template=True) 
    make = indexes.CharField() 
    section = indexes.CharField() 
    subcategory = indexes.CharField() 
    content = indexes.CharField(model_attr='content') 
    images = indexes.CharField(model_attr='images') 

    def get_model(self): 
     return Advertisement 

    def index_queryset(self, using=None): 
     return self.get_model().objects.filter(is_published=True).select_related('make').select_related('section').select_related('subcategory') 

поиск Форма:

<form action="/search" method="get"> 
     <input type="text-search" name="q"> 
     <input type="submit" value=""> 
    </form> 

шаблона:

{% block content %} 

{% for result in page.object_list %} 
    <p>{{ result.object.title }}</p> 
    <p>{{ result.object.content }}</p> 
    <p>{{ result.object.images }}</p> 
    <p>{{ result.object.make }}</p> 
    <p>{{ result.object.section }}</p> 
    <p>{{ result.object.subcategory }}</p> 
{% empty %} 
    <p>No result.</p> 
{% endfor %} 

{% endblock %} 

Глядя на curl -XGET "http://localhost:9200/_search?q=fender+boss" я получаю все значения там, где "хозяин" и "крыло"

, когда вы вводите поле поиска «босс-крыло», я не получаю результата. Из формы поиска я могу получить результат только с одним словом, например «босс». Как сделать поиск нескольких слов?

ответ

2

В этот месяц я попал в эту проблему.

Для выполнения правильного запроса вам нужно переопределить некоторые объекты сена. Я нашел эту статью очень полезной Extending Haystack’s Elasticsearch backend. Довольно сложно в начале, но как-то понять, как это работает ... это работает :-)

В статье в блоге рассказывается, как реализовать вложенный запрос elasticsearch ... ну ... Я реализовал базовый multi_match query.

# -*- coding: utf-8 -*- 
from __future__ import absolute_import 

from django.conf import settings 

from haystack.backends.elasticsearch_backend import (
    ElasticsearchSearchBackend, ElasticsearchSearchEngine, ElasticsearchSearchQuery) 
from haystack.query import SearchQuerySet 


class ElasticsearchEngineBackendCustom(ElasticsearchSearchBackend): 
    DEFAULT_ANALYZER = "snowball" 

    def __init__(self, connection_alias, **connection_options): 
     super(ElasticsearchEngineBackendCustom, self).__init__(connection_alias, **connection_options) 

     user_settings = getattr(settings, 'ELASTICSEARCH_INDEX_SETTINGS', {}) 
     if user_settings: 
      setattr(self, 'DEFAULT_SETTINGS', user_settings) 

     user_analyzer = getattr(settings, 'ELASTICSEARCH_DEFAULT_ANALYZER', '') 
     if user_analyzer: 
      setattr(self, 'DEFAULT_ANALYZER', user_analyzer) 

    def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_offset=None, 
          fields='', highlight=False, facets=None, 
          date_facets=None, query_facets=None, 
          narrow_queries=None, spelling_query=None, 
          within=None, dwithin=None, distance_point=None, 
          models=None, limit_to_registered_models=None, 
          result_class=None, multi_match=None): 

     out = super(ElasticsearchEngineBackendCustom, self).build_search_kwargs(query_string, sort_by, start_offset, 
                       end_offset, 
                       fields, highlight, facets, 
                       date_facets, query_facets, 
                       narrow_queries, spelling_query, 
                       within, dwithin, distance_point, 
                       models, limit_to_registered_models, 
                       result_class) 

     if multi_match: 
      out['query'] = { 
       'multi_match': { 
        'query': multi_match['query'], 
        'fields': multi_match['fields'], 
        'tie_breaker': multi_match['tie_breaker'], 
        'minimum_should_match': multi_match['minimum_should_match'], 
       } 
      } 

     return out 

    def build_schema(self, fields): 
     content_field_name, mapping = super(ElasticsearchEngineBackendCustom, self).build_schema(fields) 

     for field_name, field_class in fields.items(): 
      field_mapping = mapping[field_class.index_fieldname] 

      if field_mapping['type'] == 'string' and field_class.indexed: 
       if not hasattr(field_class, 'facet_for') or field_class.field_type in ('ngram', 'edge_ngram'): 
        field_mapping['analyzer'] = getattr(field_class, 'analyzer', self.DEFAULT_ANALYZER) 
      mapping.update({field_class.index_fieldname: field_mapping}) 

     return content_field_name, mapping 

    def multi_match_run(self, query, fields, minimum_should_match, tie_breaker): 
     from elasticsearch_dsl import Search 
     from elasticsearch_dsl.query import MultiMatch 

     raw = Search().using(self.conn).query(
      MultiMatch(query=u'{}'.format(query), fields=fields, minimum_should_match=minimum_should_match, tie_breaker=tie_breaker) 
     ).execute() 

     return self._process_results(raw) 


class ElasticsearchSearchQueryCustom(ElasticsearchSearchQuery): 
    def multi_match(self, query, fields, minimum_should_match, tie_breaker): 
     results = self.backend.multi_match_run(query, fields, minimum_should_match, tie_breaker) 
     self._results = results.get('results', []) 
     self._hit_count = results.get('hits', 0) 

    def add_multi_match_query(self, query, fields, minimum_should_match, tie_breaker): 
     self.multi_match_query = { 
      'query': query, 
      'fields': fields, 
      'minimum_should_match': minimum_should_match, 
      'tie_breaker': tie_breaker 
     } 

    def build_params(self, spelling_query=None, **kwargs): 
     search_kwargs = super(ElasticsearchSearchQueryCustom, self).build_params(spelling_query, **kwargs) 
     if self.multi_match_query: 
      search_kwargs['multi_match'] = self.multi_match_query 

     return search_kwargs 


class ElasticsearchSearchQuerySetCustom(SearchQuerySet): 
    def multi_match(self, query, fields, minimum_should_match="35%", tie_breaker=0.3): 
     clone = self._clone() 
     clone.query.add_multi_match_query(query, fields, minimum_should_match, tie_breaker) 
     clone.query.multi_match(query, fields, minimum_should_match, tie_breaker) 
     return clone 


class ElasticsearchEngineCustom(ElasticsearchSearchEngine): 
    backend = ElasticsearchEngineBackendCustom 
    query = ElasticsearchSearchQueryCustom 

Как вы можете видеть, что я использовал elasticsearc-dsl для выполнения запроса (MultiMatch) и эту фразу, суммирующая сообщение в блоге: ElasticsearchSearchQuerySetCustom().multi_match(...) вызова зависит от ElasticsearchSearchQueryCustom, которая зависит от ElasticsearchEngineBackendCustom.

Затем положить в настройках конфигурации elasticsearch, например:

ELASTICSEARCH_DEFAULT_ANALYZER = 'italian' 
ELASTICSEARCH_INDEX_SETTINGS = { 
    "settings": {[...]} 
} 

Вы можете захватить язык (ы) для ELASTICSEARCH_INDEX_SETTINGS от Language Analyzers

Вам необходимо переопределить также в SearchForm:

# -*- coding: utf-8 -*- 
from __future__ import absolute_import 

from haystack.forms import SearchForm 

from .backend import ElasticsearchSearchQuerySetCustom 


class SearchFormCustom(SearchForm): 
    def search(self): 
     query = self.searchqueryset.query.clean(self.cleaned_data.get('q')) 
     if not self.is_valid() or not query: 
      return self.no_query_found() 

     sqs = ElasticsearchSearchQuerySetCustom().multi_match(query, ['title^8', 'text^0.5']) 

     return sqs 

Поля title и text должны быть в вашем индексе, а символ каретки i s, используемые для повышения уровня поля.

Вам нужно переопределить шаблоны стог URL-адрес для того, чтобы использовать пользовательскую форму:

urlpatterns = patterns(
    'search.views', 
    url('^$', search_view_factory(form_class=SearchFormCustom), name='haystack-search'), 
) 

Вот это, НТН :-)

Обратите внимание не используют result.object.something, но использование вместо полей в вашем индексе, например result.tilte, потому что result.object.tilte попадает в базу данных! См. Haystack Best Practices

+0

Большое спасибо за ваш подробный ответ, но я переключился на использование и на solr – Ihar

Смежные вопросы