partial improvements to search

This commit is contained in:
Sam
2021-03-02 13:17:37 +00:00
parent 5bab1d75cb
commit 1338570c05
2 changed files with 175 additions and 106 deletions

View File

@@ -535,6 +535,37 @@ class ProductSearchTest(TestCase):
# check prices
self.assertTrue(payload['prices']['min'] <= payload['prices']['max'])
def test_anon_user_can_search_no_querystring(self):
expected_instances = [
self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"),
self.factory(tags="colores/rojos, tono/brillante"),
self.factory(tags="lunares/azules", description="zapatos rojos"),
self.factory(tags="lunares/rojos", description="zapatos"),
self.factory(attributes='"zapatos de campo", tono/oscuro'),
]
unexpected_instances = [
self.factory(description="chanclas"),
self.factory(tags="azules"),
]
q = quote("zapatos rojos")
url = f"{self.endpoint}?q="
# send in request
response = self.client.get(url)
# check response
self.assertEqual(response.status_code, 200)
# load response data
payload = response.json()
# check for expected fields in payload
self.assertIsNotNone(payload.get('filters'))
self.assertIsNotNone(payload.get('count'))
self.assertIsNotNone(payload.get('products'))
self.assertIsNotNone(payload.get('prices'))
# check for object creation
self.assertEquals(len(payload['products']), len(expected_instances) + len(unexpected_instances))
def test_anon_user_can_paginate_search(self):
expected_instances = [
self.factory(tags="lunares/rojos", category='zapatos', description="zapatos verdes"),
@@ -549,10 +580,10 @@ class ProductSearchTest(TestCase):
]
q = quote("zapatos rojos")
limit = 2
# test limit less than available
limit = 3
url = f"{self.endpoint}?q={q}&limit={limit}"
# send in request
response = self.client.get(url)
# check response
@@ -562,6 +593,44 @@ class ProductSearchTest(TestCase):
self.assertEquals(len(payload['products']), limit)
self.assertEquals(payload['count'], len(expected_instances))
# test limit less than available
limit = 10
url = f"{self.endpoint}?q={q}&limit={limit}"
response = self.client.get(url)
# check response
self.assertEqual(response.status_code, 200)
# load response data
payload = response.json()
self.assertEquals(len(payload['products']), len(expected_instances))
self.assertEquals(payload['count'], len(expected_instances))
# test limit equal to available, offset zero
limit = len(expected_instances)
offset = 0
url = f"{self.endpoint}?q={q}&limit={limit}&offset={offset}"
response = self.client.get(url)
# check response
self.assertEqual(response.status_code, 200)
# load response data
payload = response.json()
self.assertEquals(len(payload['products']), len(expected_instances))
self.assertEquals(payload['count'], len(expected_instances))
# test limit and offset equal to available
limit = len(expected_instances)
offset = limit
url = f"{self.endpoint}?q={q}&limit={limit}&offset={offset}"
response = self.client.get(url)
# check response
self.assertEqual(response.status_code, 200)
# load response data
payload = response.json()
self.assertEquals(len(payload['products']), 0)
self.assertEquals(payload['count'], len(expected_instances))
def test_anon_user_can_filter_shipping_cost_true(self):
expected_instances = [
self.factory(tags="colores/rojos, tono/brillante", shipping_cost=100.00),
@@ -685,7 +754,6 @@ class ProductSearchTest(TestCase):
]
q = quote("zapatos rojos")
# discount=true
url = f"{self.endpoint}?q={q}&category=ropa/nueva"
# send in request
response = self.client.get(url)
@@ -815,7 +883,7 @@ class ProductSearchTest(TestCase):
url = f"{self.endpoint}?q={q}&order=oldest"
# send in request
response = self.client.get(url)
import ipdb; ipdb.set_trace()
# check response
self.assertEqual(response.status_code, 200)
# load response data

View File

@@ -90,115 +90,116 @@ def load_coop_products(request):
@api_view(['GET',]) # include allowed methods
def product_search(request):
"""
Takes a string of data, return relevant products
"""
Takes a string of data, return relevant products
Params:
- q: used for search [MANDATORY]
- limit: max number of returned instances [OPTIONAL]
- offset: where to start counting results [OPTIONAL]
- shipping_cost: true/false
- discount: true/false
- category: string
- tags: string
- order: string (newest/oldest)
- price_min: int
- price_max: int
Params:
- q: used for search [MANDATORY]
- limit: max number of returned instances [OPTIONAL]
- offset: where to start counting results [OPTIONAL]
- shipping_cost: true/false
- discount: true/false
- category: string
- tags: string
- order: string (newest/oldest)
- price_min: int
- price_max: int
In the response:
- filters
- count
- products
- price_min
- price_max
In the response:
- filters
- count
- products
- price_min
- price_max
"""
# capture query params
q = request.GET.get('q', None)
limit = request.GET.get('limit', None)
offset = request.GET.get('offset', None)
shipping_cost = request.GET.get('shipping_cost', None)
if shipping_cost is not None:
if shipping_cost == 'true':
shipping_cost = True
elif shipping_cost == 'false':
shipping_cost = False
else:
shipping_cost = None
discount = request.GET.get('discount', None)
if discount is not None:
if discount == 'true':
discount = True
elif discount == 'false':
discount = False
else:
discount = None
category = request.GET.get('category', None)
tags = request.GET.get('tags', None)
price_min = request.GET.get('price_min', None)
price_max = request.GET.get('price_max', None)
order = request.GET.get('order', '')
"""
# capture query params
q = request.GET.get('q', None)
limit = request.GET.get('limit', None)
offset = request.GET.get('offset', None)
shipping_cost = request.GET.get('shipping_cost', None)
if shipping_cost is not None:
if shipping_cost == 'true':
shipping_cost = True
elif shipping_cost == 'false':
shipping_cost = False
else:
shipping_cost = None
discount = request.GET.get('discount', None)
if discount is not None:
if discount == 'true':
discount = True
elif discount == 'false':
discount = False
else:
discount = None
category = request.GET.get('category', None)
tags = request.GET.get('tags', None)
price_min = request.GET.get('price_min', None)
price_max = request.GET.get('price_max', None)
order = request.GET.get('order', '')
if q is None:
return Response({"errors": {"details": "No query string to parse"}})
elif q is '':
# return everything
serializer = ProductSerializer(Product.objects.all(), many=True)
products = serializer.data
# filters = extract_search_filters(products)
return Response(data={"filters": [], "count": len(products), "products": products})
try:
# we collect our results here
result_set = set()
# values for response
prices = {
'min': None,
'max': None,
}
if q is None:
return Response({"errors": {"details": "No query string to parse"}})
try:
# we collect our results here
result_set = set()
# values for response
prices = {
'min': None,
'max': None,
}
if q == '':
# filter entire queryset
products_qs = Product.objects.all()
if tags:
products_qs = Product.objects.filter(tags=tags)
if category:
products_qs = Product.objects.filter(category=category)
# serialize and list data
serializer = ProductSerializer(products_qs, many=True)
result_list = [dict(i) for i in serializer.data]
else:
# split query string into single words
chunks = q.split(' ')
for chunk in chunks:
product_set, min_price, max_price = find_related_products_v6(chunk, shipping_cost, discount, category, tags, price_min, price_max)
# update price values
if product_set:
# import ipdb; ipdb.set_trace()
if prices['min'] is None or min_price['price__min'] < prices['min']:
prices['min'] = min_price['price__min']
if prices['max'] is None or max_price['price__max'] > prices['max']:
prices['max'] = max_price['price__max']
# add to result set
result_set.update(product_set)
# TODO: add search for entire phrase ???
product_set, min_price, max_price = find_related_products_v6(chunk, shipping_cost, discount, category, tags, price_min, price_max)
# update price values
if product_set:
if prices['min'] is None or min_price['price__min'] < prices['min']:
prices['min'] = min_price['price__min']
if prices['max'] is None or max_price['price__max'] > prices['max']:
prices['max'] = max_price['price__max']
# add to result set
result_set.update(product_set)
# serialize and list data
serializer = SearchResultSerializer(product_set, many=True)
result_list = [dict(i) for i in serializer.data]
# extract filters from result_set
filters = extract_search_filters(result_set)
# extract filters from result_set
filters = extract_search_filters(result_set)
# order the results
if order == 'newest':
# order results by created
result_list = sorted(result_list, key= lambda x:x['created'], reverse=True)
elif order == 'oldest':
# order results by created
result_list = sorted(result_list, key= lambda x:x['created'], reverse=False)
elif q != '':
# order results by RANK
result_list = sorted(result_list, key= lambda x:x['rank'], reverse=True)
result_list = list(result_set)
if order == 'newest':
# order results by created
ordered_products = sorted(result_list, key= lambda x:x.created, reverse=True)
elif order == 'oldest':
# order results by created
ordered_products = sorted(result_list, key= lambda x:x.created, reverse=False)
else:
# order results by RANK
ordered_products = sorted(result_list, key= lambda rank:rank.rank, reverse=True)
# extract max and min price values
serializer = SearchResultSerializer(ordered_products, many=True)
product_results = [dict(i) for i in serializer.data]
total_results = len(product_results)
# RESULTS PAGINATION
if limit is not None and offset is not None:
limit = int(limit)
offset = int(offset)
product_results = product_results[offset:(limit+offset)]
elif limit is not None:
limit = int(limit)
product_results = product_results[:limit]
return Response(data={"filters": filters, "count": total_results, "products": product_results, 'prices': prices})
except Exception as e:
return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
total_results = len(result_list)
# RESULTS PAGINATION
if limit is not None and offset is not None:
limit = int(limit)
offset = int(offset)
result_list = result_list[offset:(limit+offset)]
elif limit is not None:
limit = int(limit)
result_list = result_list[:limit]
return Response(data={"filters": filters, "count": total_results, "products": result_list, 'prices': prices})
except Exception as e:
return Response({"errors": {"details": str(e)}}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)