diff --git a/wagtail/wagtailadmin/taggable.py b/wagtail/wagtailadmin/taggable.py index 39213e956..91c36ca7f 100644 --- a/wagtail/wagtailadmin/taggable.py +++ b/wagtail/wagtailadmin/taggable.py @@ -4,27 +4,20 @@ from django.contrib.contenttypes.models import ContentType from django.db.models import Count from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from wagtail.wagtailsearch import Indexed, get_search_backend +from wagtail.wagtailsearch import indexed +from wagtail.wagtailsearch.backends import get_search_backend -class TagSearchable(Indexed): +class TagSearchable(indexed.Indexed): """ Mixin to provide a 'search' method, searching on the 'title' field and tags, for models that provide those things. """ - indexed_fields = { - 'title': { - 'type': 'string', - 'analyzer': 'edgengram_analyzer', - 'boost': 10, - }, - 'get_tags': { - 'type': 'string', - 'analyzer': 'edgengram_analyzer', - 'boost': 10, - }, - } + search_fields = ( + indexed.SearchField('title', partial_match=True, boost=10), + indexed.SearchField('get_tags', partial_match=True, boost=10) + ) @property def get_tags(self): diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index cf33bcbfb..b742bfd59 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -28,7 +28,8 @@ from treebeard.mp_tree import MP_Node from wagtail.wagtailcore.utils import camelcase_to_underscore from wagtail.wagtailcore.query import PageQuerySet -from wagtail.wagtailsearch import Indexed, get_search_backend +from wagtail.wagtailsearch import indexed +from wagtail.wagtailsearch.backends import get_search_backend class SiteManager(models.Manager): @@ -260,7 +261,7 @@ class PageBase(models.base.ModelBase): @python_2_unicode_compatible -class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, Indexed)): +class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, indexed.Indexed)): title = models.CharField(max_length=255, help_text=_("The page title as you'd like it to be seen by the public")) slug = models.SlugField(help_text=_("The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/")) # TODO: enforce uniqueness on slug field per parent (will have to be done at the Django @@ -279,21 +280,11 @@ class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, Indexed)): expire_at = models.DateTimeField(verbose_name=_("Expiry date/time"), help_text=_("Please add a date-time in the form YYYY-MM-DD hh:mm."), blank=True, null=True) expired = models.BooleanField(default=False, editable=False) - indexed_fields = { - 'title': { - 'type': 'string', - 'analyzer': 'edgengram_analyzer', - 'boost': 100, - }, - 'live': { - 'type': 'boolean', - 'index': 'not_analyzed', - }, - 'path': { - 'type': 'string', - 'index': 'not_analyzed', - }, - } + search_fields = ( + indexed.SearchField('title', partial_match=True, boost=100), + indexed.FilterField('live'), + indexed.FilterField('path'), + ) def __init__(self, *args, **kwargs): super(Page, self).__init__(*args, **kwargs) diff --git a/wagtail/wagtaildocs/models.py b/wagtail/wagtaildocs/models.py index a87b9c02b..513c0ede0 100644 --- a/wagtail/wagtaildocs/models.py +++ b/wagtail/wagtaildocs/models.py @@ -12,6 +12,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import python_2_unicode_compatible from wagtail.wagtailadmin.taggable import TagSearchable +from wagtail.wagtailsearch import indexed @python_2_unicode_compatible @@ -23,14 +24,9 @@ class Document(models.Model, TagSearchable): tags = TaggableManager(help_text=None, blank=True, verbose_name=_('Tags')) - indexed_fields = { - 'uploaded_by_user_id': { - 'type': 'integer', - 'store': 'yes', - 'indexed': 'no', - 'boost': 0, - }, - } + search_fields = TagSearchable.search_fields + ( + indexed.FilterField('uploaded_by_user'), + ) def __str__(self): return self.title diff --git a/wagtail/wagtailimages/models.py b/wagtail/wagtailimages/models.py index db73b048a..c561cdcc5 100644 --- a/wagtail/wagtailimages/models.py +++ b/wagtail/wagtailimages/models.py @@ -20,6 +20,7 @@ from unidecode import unidecode from wagtail.wagtailadmin.taggable import TagSearchable from wagtail.wagtailimages.backends import get_image_backend +from wagtail.wagtailsearch import indexed from .utils import validate_image_format @@ -48,14 +49,9 @@ class AbstractImage(models.Model, TagSearchable): tags = TaggableManager(help_text=None, blank=True, verbose_name=_('Tags')) - indexed_fields = { - 'uploaded_by_user_id': { - 'type': 'integer', - 'store': 'yes', - 'indexed': 'no', - 'boost': 0, - }, - } + search_fields = TagSearchable.search_fields + ( + indexed.FilterField('uploaded_by_user'), + ) def __str__(self): return self.title diff --git a/wagtail/wagtailsearch/indexed.py b/wagtail/wagtailsearch/indexed.py index b53481727..e2817905d 100644 --- a/wagtail/wagtailsearch/indexed.py +++ b/wagtail/wagtailsearch/indexed.py @@ -35,6 +35,11 @@ class Indexed(object): @classmethod def indexed_get_indexed_fields(cls): + # New way + if hasattr(cls, 'search_fields'): + return dict((field.get_attname(cls), field.to_dict(cls)) for field in cls.search_fields) + + # Old way # Get indexed fields for this class as dictionary indexed_fields = cls.indexed_fields if isinstance(indexed_fields, dict): @@ -83,3 +88,58 @@ class Indexed(object): return doc indexed_fields = () + + +class BaseField(object): + def __init__(self, field_name, **kwargs): + self.field_name = field_name + self.kwargs = kwargs + + def get_field(self, cls): + return cls._meta.get_field_by_name(self.field_name)[0] + + def get_attname(self, cls): + try: + field = self.get_field(cls) + return field.attname + except models.fields.FieldDoesNotExist: + return self.field_name + + def to_dict(self, cls): + dic = { + 'type': 'string' + } + + if 'es_extra' in self.kwargs: + for key, value in self.kwargs['es_extra'].items(): + dic[key] = value + + return dic + + +class SearchField(BaseField): + def __init__(self, field_name, boost=None, partial_match=False, **kwargs): + super(SearchField, self).__init__(field_name, **kwargs) + self.boost = boost + self.partial_match = partial_match + + def to_dict(self, cls): + dic = super(SearchField, self).to_dict(cls) + + if self.boost and 'boost' not in dic: + dic['boost'] = self.boost + + if self.partial_match and 'analyzer' not in dic: + dic['analyzer'] = 'edgengram_analyzer' + + return dic + + +class FilterField(BaseField): + def to_dict(self, cls): + dic = super(FilterField, self).to_dict(cls) + + if 'index' not in dic: + dic['index'] = 'not_analyzed' + + return dic diff --git a/wagtail/wagtailsearch/models.py b/wagtail/wagtailsearch/models.py index dd2a0cb69..f6cfdf1dd 100644 --- a/wagtail/wagtailsearch/models.py +++ b/wagtail/wagtailsearch/models.py @@ -4,7 +4,7 @@ from django.db import models from django.utils import timezone from django.utils.encoding import python_2_unicode_compatible -from wagtail.wagtailsearch.indexed import Indexed +from wagtail.wagtailsearch import indexed from wagtail.wagtailsearch.utils import normalise_query_string, MAX_QUERY_STRING_LENGTH @@ -85,12 +85,17 @@ class EditorsPick(models.Model): # Used for tests -class SearchTest(models.Model, Indexed): +class SearchTest(models.Model, indexed.Indexed): title = models.CharField(max_length=255) content = models.TextField() live = models.BooleanField(default=False) - indexed_fields = ("title", "content", "callable_indexed_field", "live") + search_fields = ( + indexed.SearchField('title'), + indexed.SearchField('content'), + indexed.SearchField('callable_indexed_field'), + indexed.SearchField('live'), + ) def callable_indexed_field(self): return "Callable" @@ -99,4 +104,6 @@ class SearchTest(models.Model, Indexed): class SearchTestChild(SearchTest): extra_content = models.TextField() - indexed_fields = "extra_content" + search_fields = SearchTest.search_fields + ( + indexed.SearchField('extra_content'), + )