Merge branch 'feature/es' into develop

This commit is contained in:
evgeniy-st 2019-10-04 12:11:12 +03:00
commit 9fb05f0407
7 changed files with 181 additions and 82 deletions

View File

@ -11,6 +11,7 @@ from django.db import models
from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q from django.db.models import When, Case, F, ExpressionWrapper, Subquery, Q
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from elasticsearch_dsl import Q
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
from collection.models import Collection from collection.models import Collection
@ -98,6 +99,16 @@ class EstablishmentQuerySet(models.QuerySet):
else: else:
return self.none() return self.none()
def es_search(self, value, locale=None):
"""Search text via ElasticSearch."""
from search_indexes.documents import EstablishmentDocument
search = EstablishmentDocument.search().filter(
Q('match', name=value) |
Q('match', **{f'description.{locale}': value})
).execute()
ids = [result.meta.id for result in search]
return self.filter(id__in=ids)
def by_country_code(self, code): def by_country_code(self, code):
"""Return establishments by country code""" """Return establishments by country code"""
return self.filter(address__city__country__code=code) return self.filter(address__city__country__code=code)
@ -330,7 +341,6 @@ class Establishment(ProjectBaseMixin, URLImageMixin, TranslatedFieldsMixin):
raise ValidationError('Establishment type of subtype does not match') raise ValidationError('Establishment type of subtype does not match')
self.establishment_subtypes.add(establishment_subtype) self.establishment_subtypes.add(establishment_subtype)
@property @property
def vintage_year(self): def vintage_year(self):
last_review = self.reviews.by_status(Review.READY).last() last_review = self.reviews.by_status(Review.READY).last()

View File

@ -5,9 +5,7 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.reverse import reverse from rest_framework.reverse import reverse
from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin
from django.contrib.contenttypes.models import ContentType
from rating.models import Rating from rating.models import Rating
from random import sample as random_sample
class NewsType(models.Model): class NewsType(models.Model):

View File

@ -14,6 +14,7 @@ EstablishmentIndex.settings(number_of_shards=1, number_of_replicas=1)
class EstablishmentDocument(Document): class EstablishmentDocument(Document):
"""Establishment document.""" """Establishment document."""
preview_image = fields.KeywordField(attr='preview_image_url')
description = fields.ObjectField(attr='description_indexing', description = fields.ObjectField(attr='description_indexing',
properties=OBJECT_FIELD_PROPERTIES) properties=OBJECT_FIELD_PROPERTIES)
establishment_type = fields.ObjectField( establishment_type = fields.ObjectField(
@ -50,7 +51,8 @@ class EstablishmentDocument(Document):
fields={'raw': fields.KeywordField()} fields={'raw': fields.KeywordField()}
), ),
'number': fields.IntegerField(), 'number': fields.IntegerField(),
'location': fields.GeoPointField(attr='location_field_indexing'), 'coordinates': fields.GeoPointField(attr='location_field_indexing'),
# todo: remove if not used
'city': fields.ObjectField( 'city': fields.ObjectField(
properties={ properties={
'id': fields.IntegerField(), 'id': fields.IntegerField(),
@ -82,9 +84,10 @@ class EstablishmentDocument(Document):
fields = ( fields = (
'id', 'id',
'name', 'name',
'toque_number', 'name_translated',
'price_level', 'price_level',
'preview_image_url', 'toque_number',
'public_mark',
'slug', 'slug',
) )

View File

@ -13,18 +13,26 @@ NewsIndex.settings(number_of_shards=1, number_of_replicas=1)
class NewsDocument(Document): class NewsDocument(Document):
"""News document.""" """News document."""
news_type = fields.NestedField(properties={ news_type = fields.ObjectField(properties={'id': fields.IntegerField(),
'id': fields.IntegerField(), 'name': fields.KeywordField()})
'name': fields.KeywordField() title = fields.ObjectField(attr='title_indexing',
}) properties=OBJECT_FIELD_PROPERTIES)
title = fields.ObjectField(properties=OBJECT_FIELD_PROPERTIES) subtitle = fields.ObjectField(attr='subtitle_indexing',
subtitle = fields.ObjectField(properties=OBJECT_FIELD_PROPERTIES) properties=OBJECT_FIELD_PROPERTIES)
description = fields.ObjectField(properties=OBJECT_FIELD_PROPERTIES) description = fields.ObjectField(attr='description_indexing',
country = fields.NestedField(properties={ properties=OBJECT_FIELD_PROPERTIES)
'id': fields.IntegerField(), country = fields.ObjectField(properties={'id': fields.IntegerField(),
'code': fields.KeywordField() 'code': fields.KeywordField()})
})
web_url = fields.KeywordField(attr='web_url') web_url = fields.KeywordField(attr='web_url')
tags = fields.ObjectField(
properties={
'id': fields.IntegerField(attr='metadata.id'),
'label': fields.ObjectField(attr='metadata.label_indexing',
properties=OBJECT_FIELD_PROPERTIES),
'category': fields.ObjectField(attr='metadata.category',
properties={'id': fields.IntegerField()})
},
multi=True)
class Django: class Django:
@ -32,20 +40,19 @@ class NewsDocument(Document):
fields = ( fields = (
'id', 'id',
'playlist', 'playlist',
'start',
'end',
'slug',
'state',
'is_highlighted',
'image_url',
'preview_image_url',
'template',
) )
related_models = [models.NewsType] related_models = [models.NewsType]
def get_queryset(self): def get_queryset(self):
return super().get_queryset().published() return super().get_queryset().published().with_base_related()
def prepare_title(self, instance):
return instance.title
def prepare_subtitle(self, instance):
return instance.subtitle
def prepare_description(self, instance):
return instance.description
def get_instances_from_related(self, related_instance): def get_instances_from_related(self, related_instance):
"""If related_models is set, define how to retrieve the Car instance(s) from the related model. """If related_models is set, define how to retrieve the Car instance(s) from the related model.

View File

@ -1,16 +1,42 @@
"""Search indexes serializers.""" """Search indexes serializers."""
from rest_framework import serializers from rest_framework import serializers
from django_elasticsearch_dsl_drf.serializers import DocumentSerializer from django_elasticsearch_dsl_drf.serializers import DocumentSerializer
from news.serializers import NewsTypeSerializer
from search_indexes.documents import EstablishmentDocument, NewsDocument from search_indexes.documents import EstablishmentDocument, NewsDocument
from search_indexes.utils import get_translated_value from search_indexes.utils import get_translated_value
class TagsDocumentSerializer(serializers.Serializer):
"""Tags serializer for ES Document."""
id = serializers.IntegerField()
label_translated = serializers.SerializerMethodField()
def get_label_translated(self, obj):
return get_translated_value(obj.label)
class AddressDocumentSerializer(serializers.Serializer):
"""Address serializer for ES Document."""
id = serializers.IntegerField()
street_name_1 = serializers.CharField()
street_name_2 = serializers.CharField()
number = serializers.IntegerField()
postal_code = serializers.CharField()
latitude = serializers.FloatField(allow_null=True, source='coordinates.lat')
longitude = serializers.FloatField(allow_null=True, source='coordinates.lon')
geo_lon = serializers.FloatField(allow_null=True, source='coordinates.lon')
geo_lat = serializers.FloatField(allow_null=True, source='coordinates.lat')
class NewsDocumentSerializer(DocumentSerializer): class NewsDocumentSerializer(DocumentSerializer):
"""News document serializer.""" """News document serializer."""
title_translated = serializers.SerializerMethodField(allow_null=True) title_translated = serializers.SerializerMethodField(allow_null=True)
subtitle_translated = serializers.SerializerMethodField(allow_null=True) subtitle_translated = serializers.SerializerMethodField(allow_null=True)
description_translated = serializers.SerializerMethodField(allow_null=True) news_type = NewsTypeSerializer()
tags = TagsDocumentSerializer(many=True)
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -18,13 +44,14 @@ class NewsDocumentSerializer(DocumentSerializer):
document = NewsDocument document = NewsDocument
fields = ( fields = (
'id', 'id',
'title',
'subtitle',
'description',
'web_url',
'title_translated', 'title_translated',
'subtitle_translated', 'subtitle_translated',
'description_translated', 'is_highlighted',
'image_url',
'preview_image_url',
'news_type',
'tags',
'slug',
) )
@staticmethod @staticmethod
@ -35,18 +62,13 @@ class NewsDocumentSerializer(DocumentSerializer):
def get_subtitle_translated(obj): def get_subtitle_translated(obj):
return get_translated_value(obj.subtitle) return get_translated_value(obj.subtitle)
@staticmethod
def get_description_translated(obj):
return get_translated_value(obj.description)
# todo: country_name_translated
class EstablishmentDocumentSerializer(DocumentSerializer): class EstablishmentDocumentSerializer(DocumentSerializer):
"""Establishment document serializer.""" """Establishment document serializer."""
description_translated = serializers.SerializerMethodField(allow_null=True) address = AddressDocumentSerializer()
tags = TagsDocumentSerializer(many=True)
preview_image = serializers.URLField(source='preview_image_url')
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -54,43 +76,40 @@ class EstablishmentDocumentSerializer(DocumentSerializer):
fields = ( fields = (
'id', 'id',
'name', 'name',
'public_mark', 'name_translated',
'toque_number',
'price_level', 'price_level',
'description_translated', 'toque_number',
'tags', 'public_mark',
'address',
'collections',
'establishment_type',
'establishment_subtypes',
'preview_image',
'slug', 'slug',
'preview_image',
'address',
'tags',
# 'collections',
# 'establishment_type',
# 'establishment_subtypes',
) )
@staticmethod
def get_description_translated(obj):
return get_translated_value(obj.description)
def to_representation(self, instance): # def to_representation(self, instance):
ret = super().to_representation(instance) # ret = super().to_representation(instance)
dict_merge = lambda a, b: a.update(b) or a # dict_merge = lambda a, b: a.update(b) or a
#
ret['tags'] = map(lambda tag: dict_merge(tag, {'label_translated': get_translated_value(tag.pop('label'))}), # ret['tags'] = map(lambda tag: dict_merge(tag, {'label_translated': get_translated_value(tag.pop('label'))}),
ret['tags']) # ret['tags'])
ret['establishment_subtypes'] = map( # ret['establishment_subtypes'] = map(
lambda subtype: dict_merge(subtype, {'name_translated': get_translated_value(subtype.pop('name'))}), # lambda subtype: dict_merge(subtype, {'name_translated': get_translated_value(subtype.pop('name'))}),
ret['establishment_subtypes']) # ret['establishment_subtypes'])
if ret.get('establishment_type'): # if ret.get('establishment_type'):
ret['establishment_type']['name_translated'] = get_translated_value(ret['establishment_type'].pop('name')) # ret['establishment_type']['name_translated'] = get_translated_value(ret['establishment_type'].pop('name'))
if ret.get('address'): # if ret.get('address'):
ret['address']['city']['country']['name_translated'] = get_translated_value( # ret['address']['city']['country']['name_translated'] = get_translated_value(
ret['address']['city']['country'].pop('name')) # ret['address']['city']['country'].pop('name'))
location = ret['address'].pop('location') # location = ret['address'].pop('location')
if location: # if location:
ret['address']['geo_lon'] = location['lon'] # ret['address']['geo_lon'] = location['lon']
ret['address']['geo_lat'] = location['lat'] # ret['address']['geo_lat'] = location['lat']
#
ret['type'] = ret.pop('establishment_type') # ret['type'] = ret.pop('establishment_type')
ret['subtypes'] = ret.pop('establishment_subtypes') # ret['subtypes'] = ret.pop('establishment_subtypes')
#
return ret # return ret

View File

@ -1,5 +1,5 @@
"""Search indexes app signals.""" """Search indexes app signals."""
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django_elasticsearch_dsl.registries import registry from django_elasticsearch_dsl.registries import registry
@ -17,17 +17,65 @@ def update_document(sender, **kwargs):
address__city__country=instance) address__city__country=instance)
for establishment in establishments: for establishment in establishments:
registry.update(establishment) registry.update(establishment)
if model_name == 'city': if model_name == 'city':
establishments = Establishment.objects.filter( establishments = Establishment.objects.filter(
address__city=instance) address__city=instance)
for establishment in establishments: for establishment in establishments:
registry.update(establishment) registry.update(establishment)
if model_name == 'address': if model_name == 'address':
establishments = Establishment.objects.filter( establishments = Establishment.objects.filter(
address=instance) address=instance)
for establishment in establishments: for establishment in establishments:
registry.update(establishment) registry.update(establishment)
# todo: delete document if app_label == 'establishment':
if model_name == 'establishmenttype':
establishments = Establishment.objects.filter(
establishment_type=instance)
for establishment in establishments:
registry.update(establishment)
if model_name == 'establishmentsubtype':
establishments = Establishment.objects.filter(
establishment_subtypes=instance)
for establishment in establishments:
registry.update(establishment)
if app_label == 'main':
if model_name == 'metadata':
establishments = Establishment.objects.filter(tags__metadata=instance)
for establishment in establishments:
registry.update(establishment)
if model_name == 'metadatacontent':
establishments = Establishment.objects.filter(tags=instance)
for establishment in establishments:
registry.update(establishment)
@receiver(post_save)
def update_news(sender, **kwargs):
from news.models import News
app_label = sender._meta.app_label
model_name = sender._meta.model_name
instance = kwargs['instance']
if app_label == 'location':
if model_name == 'country':
qs = News.objects.filter(country=instance)
for news in qs:
registry.update(news)
if app_label == 'news':
if model_name == 'newstype':
qs = News.objects.filter(news_type=instance)
for news in qs:
registry.update(news)
if app_label == 'main':
if model_name == 'metadata':
qs = News.objects.filter(tags__metadata=instance)
for news in qs:
registry.update(news)
if model_name == 'metadatacontent':
qs = News.objects.filter(tags=instance)
for news in qs:
registry.update(news)

View File

@ -1,8 +1,10 @@
"""Search indexes app views.""" """Search indexes app views."""
from rest_framework import permissions from rest_framework import permissions
from django_elasticsearch_dsl_drf import constants from django_elasticsearch_dsl_drf import constants
from django_elasticsearch_dsl_drf.filter_backends import (FilteringFilterBackend, from django_elasticsearch_dsl_drf.filter_backends import (
GeoSpatialFilteringFilterBackend) FilteringFilterBackend,
GeoSpatialFilteringFilterBackend
)
from django_elasticsearch_dsl_drf.viewsets import BaseDocumentViewSet from django_elasticsearch_dsl_drf.viewsets import BaseDocumentViewSet
from utils.pagination import ProjectPageNumberPagination from utils.pagination import ProjectPageNumberPagination
@ -22,6 +24,7 @@ class NewsDocumentViewSet(BaseDocumentViewSet):
filter_backends = [ filter_backends = [
filters.CustomSearchFilterBackend, filters.CustomSearchFilterBackend,
FilteringFilterBackend,
] ]
search_fields = ( search_fields = (
@ -35,6 +38,16 @@ class NewsDocumentViewSet(BaseDocumentViewSet):
'description', 'description',
) )
filter_fields = {
'tag': {
'field': 'tags.id',
'lookups': [
constants.LOOKUP_QUERY_IN,
]
},
'slug': 'slug',
}
class EstablishmentDocumentViewSet(BaseDocumentViewSet): class EstablishmentDocumentViewSet(BaseDocumentViewSet):
"""Establishment document ViewSet.""" """Establishment document ViewSet."""
@ -60,6 +73,7 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
'description', 'description',
) )
filter_fields = { filter_fields = {
'slug': 'slug',
'tag': { 'tag': {
'field': 'tags.id', 'field': 'tags.id',
'lookups': [constants.LOOKUP_QUERY_IN] 'lookups': [constants.LOOKUP_QUERY_IN]
@ -111,7 +125,7 @@ class EstablishmentDocumentViewSet(BaseDocumentViewSet):
geo_spatial_filter_fields = { geo_spatial_filter_fields = {
'location': { 'location': {
'field': 'address.location', 'field': 'address.coordinates',
'lookups': [ 'lookups': [
constants.LOOKUP_FILTER_GEO_BOUNDING_BOX, constants.LOOKUP_FILTER_GEO_BOUNDING_BOX,
] ]