Skip to content

Instantly share code, notes, and snippets.

@GrigoriyMikhalkin
Created November 29, 2015 17:13
Show Gist options
  • Save GrigoriyMikhalkin/f76be703bc53380986a0 to your computer and use it in GitHub Desktop.
Save GrigoriyMikhalkin/f76be703bc53380986a0 to your computer and use it in GitHub Desktop.
from django.conf import settings
from .query import (ConfigurableSearchQuerySet,
ConfigurableElasticSearchQuery)
from haystack.backends.elasticsearch_backend import \
ElasticsearchSearchBackend, ElasticsearchSearchEngine
# It's modification of ElasticStack code
# https://github.com/bennylope/elasticstack/blob/master/elasticstack/backends.py
class ConfigurableElasticBackend(ElasticsearchSearchBackend):
"""
Extends the Haystack ElasticSearch backend to allow configuration of index
mappings and field-by-field analyzers.
"""
DEFAULT_ANALYZER = "snowball"
def __init__(self, connection_alias, **connection_options):
super(ConfigurableElasticBackend, self).__init__(connection_alias, **connection_options)
user_settings = getattr(settings, "ELASTICSEARCH_INDEX_SETTINGS", None)
user_analyzer = getattr(settings, "ELASTICSEARCH_DEFAULT_ANALYZER", None)
#user_field_mapping = getattr(settings, "ELASTICSEARCH_")
if user_settings:
setattr(self, "DEFAULT_SETTINGS", user_settings)
if user_analyzer:
setattr(self, "DEFAULT_ANALYZER", user_analyzer)
def build_schema(self, fields):
content_field_name, mapping = super(ConfigurableElasticBackend, 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") and not field_class.field_type in("ngram", "edge_ngram"):
field_mapping["analyzer"] = getattr(field_class, "analyzer", self.DEFAULT_ANALYZER)
additional_options = getattr(field_class, "add", None)
field_mapping.update( additional_options if additional_options else {} )
mapping.update({field_class.index_fieldname: field_mapping})
return (content_field_name, mapping)
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, search_text=None):
out = super(ConfigurableElasticBackend, 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)
try:
out["sort"]
except KeyError:
out["sort"] = [{
"_script": {
"script_file": "score_script",
"type": "number",
"order": "asc",
"params": {
"q": search_text["search_text"]
}
}
}]
return out
class ConfigurableElasticSearchEngine(ElasticsearchSearchEngine):
backend = ConfigurableElasticBackend
query = ConfigurableElasticSearchQuery
from haystack.fields import CharField as BaseCharField
# It's modification of ElasticStack code
# https://github.com/bennylope/elasticstack/blob/master/elasticstack/fields.py
class ConfigurableFieldMixin(object):
"""
A mixin which allows specifying the analyzer on a per field basis.
"""
def __init__(self, **kwargs):
self.analyzer = kwargs.pop("analyzer", None)
self.add = kwargs.pop("add", None)
if self.analyzer is None:
raise ValueError("Configurable fields must have an analyzer type")
super(ConfigurableFieldMixin, self).__init__(**kwargs)
class CharField(ConfigurableFieldMixin, BaseCharField):
pass
from haystack.query import SearchQuerySet
from haystack.constants import DEFAULT_ALIAS, DJANGO_CT
from haystack.backends.elasticsearch_backend import ElasticsearchSearchQuery
class ConfigurableSearchQuerySet(SearchQuerySet):
def custom_search(self,search_text=None):
clone=self._clone()
clone.query.add_custom_search(search_text)
return clone
class ConfigurableElasticSearchQuery(ElasticsearchSearchQuery):
def __init__(self,using=DEFAULT_ALIAS):
out=super(ConfigurableElasticSearchQuery,self).__init__(using)
self.search_text = {}
def add_custom_search(self,search_text=None):
self.search_text = { "search_text": search_text }
def build_params(self,spelling_query=None,**kwargs):
search_kwargs = super(ConfigurableElasticSearchQuery,self).build_params(spelling_query, **kwargs)
search_kwargs["search_text"] = self.search_text
return search_kwargs
def _clone(self,klass=None,using=None):
clone = super(ConfigurableElasticSearchQuery,self)._clone(klass,using)
clone.search_text = self.search_text
return clone
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment