added queried search

This commit is contained in:
Alexander Karpov 2022-10-22 05:07:25 +03:00
parent bbcf4bd2a7
commit beab74a4f4
8 changed files with 117 additions and 46 deletions

View File

@ -60,6 +60,7 @@ DJANGO_APPS = [
"django.contrib.humanize", "django.contrib.humanize",
"django.contrib.admin", "django.contrib.admin",
"django.forms", "django.forms",
"django.contrib.postgres",
] ]
THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "drf_yasg"] THIRD_PARTY_APPS = ["rest_framework", "corsheaders", "drf_yasg"]

View File

@ -2,9 +2,19 @@ from rest_framework import serializers
from django.core.validators import MinLengthValidator, MinValueValidator from django.core.validators import MinLengthValidator, MinValueValidator
class QueryFilterSerializer(serializers.Serializer):
value = serializers.CharField(max_length=100)
type = serializers.CharField(max_length=100)
def create(self, validated_data):
raise NotImplementedError
def update(self, instance, validated_data):
raise NotImplementedError
class SearchSerializer(serializers.Serializer): class SearchSerializer(serializers.Serializer):
body = serializers.CharField(max_length=200) body = serializers.ListSerializer(child=QueryFilterSerializer())
def create(self, validated_data): def create(self, validated_data):
raise NotImplementedError raise NotImplementedError

View File

@ -5,47 +5,60 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from search.api.serializers import HintRequestSerializer from search.api.serializers import HintRequestSerializer
from search.api.serializers import SearchSerializer, ResponseSerializer, HintResponseSerializer, AutoCompleteRequestSerializer, AutoCompleteResponseSerializer from search.api.serializers import (
from search.services.search import process_string SearchSerializer,
ResponseSerializer,
HintResponseSerializer,
AutoCompleteRequestSerializer,
AutoCompleteResponseSerializer,
)
from search.services.search import process_search
from search.services.autocomplete_schema import autocomplete_schema from search.services.autocomplete_schema import autocomplete_schema
from search.services.hints import get_hints from search.services.hints import get_hints
user_response = openapi.Response("search results", ResponseSerializer) user_response = openapi.Response("search results", ResponseSerializer)
hint_response = openapi.Response("hints", HintResponseSerializer) hint_response = openapi.Response("hints", HintResponseSerializer)
autocomplete_response = openapi.Response("autocomplete schema", AutoCompleteResponseSerializer) autocomplete_response = openapi.Response(
"autocomplete schema", AutoCompleteResponseSerializer
)
class SearchApi(APIView): class SearchApi(APIView):
@swagger_auto_schema(request_body=SearchSerializer, responses={200: user_response}) @swagger_auto_schema(request_body=SearchSerializer, responses={200: user_response})
def post(self, request, format=None): def post(self, request):
serializer = SearchSerializer(data=request.data) serializer = SearchSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
return Response( return Response(
process_string(serializer.data["body"]), status=status.HTTP_200_OK process_search(serializer.data["body"]), status=status.HTTP_200_OK
) )
class HintApi(APIView): class HintApi(APIView):
@swagger_auto_schema(request_body=HintRequestSerializer, responses={200: hint_response}) @swagger_auto_schema(
def post(self, request, format=None): request_body=HintRequestSerializer, responses={200: hint_response}
)
def post(self, request):
serializer = HintRequestSerializer(data=request.data) serializer = HintRequestSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
return Response( return Response(
{ {
'type': get_hints(serializer.data['content']), "type": get_hints(serializer.data["content"]),
'value': serializer.data['content'] "value": serializer.data["content"],
}, },
status=status.HTTP_200_OK status=status.HTTP_200_OK,
) )
class AutoCompleteApi(APIView): class AutoCompleteApi(APIView):
@swagger_auto_schema(request_body=AutoCompleteRequestSerializer, responses={200: autocomplete_response}) @swagger_auto_schema(
def post(self, request, format=None): request_body=AutoCompleteRequestSerializer,
responses={200: autocomplete_response},
)
def post(self, request):
serializer = AutoCompleteRequestSerializer(data=request.data) serializer = AutoCompleteRequestSerializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
return Response( return Response(
{ {"nodes": autocomplete_schema(serializer.data["content"])},
'nodes': autocomplete_schema(serializer.data['content']) status=status.HTTP_200_OK,
}, status=status.HTTP_200_OK
) )

View File

@ -49,6 +49,8 @@ class Product(models.Model):
Category, related_name="products", on_delete=models.CASCADE Category, related_name="products", on_delete=models.CASCADE
) )
# score = models.IntegerField(default=0)
def __str__(self): def __str__(self):
return str(self.name) return str(self.name)
@ -56,13 +58,17 @@ class Product(models.Model):
return { return {
"name": self.name, "name": self.name,
"characteristic": [ "characteristic": [
x.serialize_self() for x in self.characteristics.objects.all() x.characteristic.serialize_self() for x in self.characteristics.all()
] ]
+ [x.serialize_self() for x in self.unit_characteristics.objects.all()], + [
x.characteristic.serialize_self()
for x in self.unit_characteristics.all()
],
} }
class Meta: class Meta:
db_table = "product" db_table = "product"
# ordering = ["score"]
class ProductCharacteristic(models.Model): class ProductCharacteristic(models.Model):

View File

@ -1,37 +1,38 @@
from search.models import Product, Category, Characteristic from search.models import Product, Category, Characteristic
def autocomplete_schema(val: str): def autocomplete_schema(val: str):
schema = [] schema = []
schema.extend( schema.extend(
[ [
{ {
'coordinate': product['name'].index(val), "coordinate": product["name"].index(val),
'value': { "value": {
'type': 'Name', "type": "Name",
'value': product['name'], "value": product["name"],
} },
} for product in Product.objects.filter(name__contains=val).values('name')] }
) for product in Product.objects.filter(name__contains=val).values("name")
schema.extend(
[
{
'coordinate': cat['name'].index(val),
'value': {
'type': 'Category',
'value': cat['name']
}
} for cat in Category.objects.filter(name__contains=val).values('name')
] ]
) )
schema.extend( schema.extend(
[ [
{ {
'coordinate': char.name.index(val), "coordinate": cat["name"].index(val),
'value': { "value": {"type": "Category", "value": cat["name"]},
'type': char.name, }
'value': char.value for cat in Category.objects.filter(name__contains=val).values("name")
} ]
} for char in Characteristic.objects.filter(name__contains=val).values('name', 'value') )
schema.extend(
[
{
"coordinate": char.name.index(val),
"value": {"type": char.name, "value": char.value},
}
for char in Characteristic.objects.filter(name__contains=val).values(
"name", "value"
)
] ]
) )
return schema return schema

View File

@ -2,11 +2,11 @@ from search.models import Product, Category, Characteristic
def get_hints(content: str) -> str: def get_hints(content: str) -> str:
category = 'Unknown' category = "Unknown"
if content in list(map(lambda product: product.name, Product.objects.all())): if content in list(map(lambda product: product.name, Product.objects.all())):
category = 'Name' category = "Name"
elif content in list(map(lambda category: category.name, Category.objects.all())): elif content in list(map(lambda category: category.name, Category.objects.all())):
category = 'Category' category = "Category"
elif content in list(map(lambda char: char.value, Characteristic.objects.all())): elif content in list(map(lambda char: char.value, Characteristic.objects.all())):
category = Characteristic.objects.get(value=content).name category = Characteristic.objects.get(value=content).name
return category return category

View File

@ -1,6 +1,44 @@
from search.models import Product from search.models import Product, Characteristic, ProductCharacteristic
from typing import List from typing import List
def process_string(text: str) -> List[dict]: def process_search(data: List[dict]) -> List[dict]:
return [x.serialize_self() for x in Product.objects.filter(name__contains=text)[5:]] prep_data = []
prep_dict = {}
prep_dict_char_type = {}
for x in data:
dat = dict(x)
if x["type"] in ["Name", "Category", "Unknown"]:
prep_data.append(dat)
else:
if x["type"] in list(prep_dict.keys()):
prep_dict[x["type"]] = prep_dict[
x["type"]
] | ProductCharacteristic.objects.filter(
characteristic__in=prep_dict_char_type[x["type"]],
characteristic__value__unaccent__trigram_similar=x["value"],
)
else:
prep_dict_char_type[x["type"]] = Characteristic.objects.filter(
name__contains=x["type"]
)
prep_dict[x["type"]] = ProductCharacteristic.objects.filter(
characteristic__in=prep_dict_char_type[x["type"]],
characteristic__value__unaccent__trigram_similar=x["value"],
)
for el, val in prep_dict.items():
prep_data.append({"type": el, "value": val})
qs = Product.objects.filter()
for x in prep_data:
typ = x["type"]
val = x["value"]
if typ == "Name":
qs = qs.filter(name__unaccent__trigram_similar=val)
elif typ == "Category":
qs = qs.filter(category__name__unaccent__trigram_similar=val)
elif typ == "Unknown":
continue
else:
qs = qs.filter(characteristics__in=val)
return [x.serialize_self() for x in qs[:5]]

2
pg.sql Normal file
View File

@ -0,0 +1,2 @@
CREATE EXTENSION unaccent;
CREATE EXTENSION pg_trgm;